IKVM11  11
Java SE 11 Virtual Machine for .NET
Loading...
Searching...
No Matches
NativeInvokerBytecodeGenerator.cs
Go to the documentation of this file.
1/*
2 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26// [IKVM] Based on original from OpenJDK, but heavily modified to directly generate a DynamicMethod,
27// instead of Java bytecode.
28// Copyright (C) 2015 Jeroen Frijters
29
30using System;
31using System.Collections.Generic;
32using System.Reflection;
33using System.Reflection.Emit;
34
35using IKVM.Attributes;
36
37using java.lang.invoke;
38
39#if !FIRST_PASS
40using BasicType = global::java.lang.invoke.LambdaForm.BasicType;
41using Class = global::java.lang.Class;
42using Name = global::java.lang.invoke.LambdaForm.Name;
43using Opcodes = jdk.@internal.org.objectweb.asm.Opcodes;
44using VerifyType = global::sun.invoke.util.VerifyType;
45using Wrapper = global::sun.invoke.util.Wrapper;
46#endif
47
49{
50
52 {
53
54#if FIRST_PASS
55
56 public static MemberName generateCustomizedCode(LambdaForm form, MethodType invokerType)
57 {
58 return null;
59 }
60
61#else
62
63 readonly RuntimeContext context;
64 readonly java.lang.invoke.LambdaForm lambdaForm;
65 readonly java.lang.invoke.MethodType invokerType;
66 readonly Type delegateType;
67 readonly DynamicMethod dm;
68 readonly CodeEmitter ilgen;
69 readonly int packedArgPos;
70 readonly Type packedArgType;
71 readonly CodeEmitterLocal[] locals;
72 readonly List<object> constants = new List<object>();
73
74 private enum Bailout
75 {
76 NotBasicType,
77 UnsupportedIntrinsic,
78 UnsupportedArrayType,
79 UnsupportedRefKind,
80 UnsupportedConstant,
81 NotStaticallyInvocable,
82 PreconditionViolated,
83 }
84
85 private sealed class BailoutException : Exception
86 {
87
88 internal BailoutException(Bailout reason, object data)
89 : base("BAILOUT " + reason + ": " + data)
90 {
91
92 }
93
94 }
95
104 NativeInvokerBytecodeGenerator(RuntimeContext context, java.lang.invoke.LambdaForm lambdaForm, global::java.lang.invoke.MethodType invokerType)
105 {
106 this.context = context ?? throw new ArgumentNullException(nameof(context));
107 this.lambdaForm = lambdaForm;
108 this.invokerType = invokerType;
109
110 if (invokerType != invokerType.basicType())
111 throw new BailoutException(Bailout.NotBasicType, invokerType);
112
113 this.delegateType = context.MethodHandleUtil.GetMemberWrapperDelegateType(invokerType);
114 var mi = context.MethodHandleUtil.GetDelegateInvokeMethod(delegateType);
115 var paramTypes = MethodHandleUtil.GetParameterTypes(typeof(object[]), mi);
116
117 // HACK the code we generate is not verifiable (known issue: locals aren't typed correctly), so we stick the DynamicMethod into mscorlib (a security critical assembly)
118 this.dm = new DynamicMethod(lambdaForm.debugName, mi.ReturnType, paramTypes, typeof(object).Module, true);
119 this.ilgen = context.CodeEmitterFactory.Create(this.dm);
120 if (invokerType.parameterCount() > MethodHandleUtil.MaxArity)
121 {
122 this.packedArgType = paramTypes[paramTypes.Length - 1];
123 this.packedArgPos = paramTypes.Length - 1;
124 }
125 else
126 {
127 this.packedArgPos = Int32.MaxValue;
128 }
129
130 locals = new CodeEmitterLocal[lambdaForm.names.Length];
131 for (int i = lambdaForm._arity(); i < lambdaForm.names.Length; i++)
132 {
133 Name name = lambdaForm.names[i];
134 if (name.index() != i)
135 {
136 throw new BailoutException(Bailout.PreconditionViolated, "name.index() != i");
137 }
138 switch (name.typeChar())
139 {
140 case 'L':
141 locals[i] = ilgen.DeclareLocal(context.Types.Object);
142 break;
143 case 'I':
144 locals[i] = ilgen.DeclareLocal(context.Types.Int32);
145 break;
146 case 'J':
147 locals[i] = ilgen.DeclareLocal(context.Types.Int64);
148 break;
149 case 'F':
150 locals[i] = ilgen.DeclareLocal(context.Types.Single);
151 break;
152 case 'D':
153 locals[i] = ilgen.DeclareLocal(context.Types.Double);
154 break;
155 case 'V':
156 break;
157 default:
158 throw new BailoutException(Bailout.PreconditionViolated, "Unsupported typeChar(): " + name.typeChar());
159 }
160 }
161 }
162
163 /*
164 * Low-level emit helpers.
165 */
166 private void emitConst(object con)
167 {
168 if (con == null)
169 {
170 ilgen.Emit(OpCodes.Ldnull);
171 }
172 else if (con is string)
173 {
174 ilgen.Emit(OpCodes.Ldstr, (string)con);
175 }
176 else if (con is java.lang.Integer)
177 {
178 ilgen.EmitLdc_I4(((java.lang.Integer)con).intValue());
179 }
180 else if (con is java.lang.Long)
181 {
182 ilgen.EmitLdc_I8(((java.lang.Long)con).longValue());
183 }
184 else if (con is java.lang.Float)
185 {
186 ilgen.EmitLdc_R4(((java.lang.Float)con).floatValue());
187 }
188 else if (con is java.lang.Double)
189 {
190 ilgen.EmitLdc_R8(((java.lang.Double)con).doubleValue());
191 }
192 else if (con is java.lang.Boolean)
193 {
194 ilgen.EmitLdc_I4(((java.lang.Boolean)con).booleanValue() ? 1 : 0);
195 }
196 else
197 {
198 throw new BailoutException(Bailout.UnsupportedConstant, con);
199 }
200 }
201
202 private void emitIconstInsn(int i)
203 {
204 ilgen.EmitLdc_I4(i);
205 }
206
207 /*
208 * NOTE: These load/store methods use the localsMap to find the correct index!
209 */
210 private void emitLoadInsn(BasicType type, int index)
211 {
212 // [IKVM] we don't need the localsMap (it is used to correct for long/double taking two slots)
213 if (locals[index] == null)
214 {
215 context.MethodHandleUtil.LoadPackedArg(ilgen, index, 1, packedArgPos, packedArgType);
216 }
217 else
218 {
219 ilgen.Emit(OpCodes.Ldloc, locals[index]);
220 }
221 }
222
223 private void emitStoreInsn(BasicType type, int index)
224 {
225 ilgen.Emit(OpCodes.Stloc, locals[index]);
226 }
227
228 private void emitAstoreInsn(int index)
229 {
230 emitStoreInsn(BasicType.L_TYPE, index);
231 }
232
233 private byte arrayTypeCode(Wrapper elementType)
234 {
235 switch (elementType.name())
236 {
237 case "BOOLEAN": return Opcodes.T_BOOLEAN;
238 case "BYTE": return Opcodes.T_BYTE;
239 case "CHAR": return Opcodes.T_CHAR;
240 case "SHORT": return Opcodes.T_SHORT;
241 case "INT": return Opcodes.T_INT;
242 case "LONG": return Opcodes.T_LONG;
243 case "FLOAT": return Opcodes.T_FLOAT;
244 case "DOUBLE": return Opcodes.T_DOUBLE;
245 case "OBJECT": return 0; // in place of Opcodes.T_OBJECT
246 default: throw new BailoutException(Bailout.PreconditionViolated, "elemendType = " + elementType);
247 }
248 }
249
250 private OpCode arrayInsnOpcode(byte tcode)
251 {
252 switch (tcode)
253 {
254 case Opcodes.T_BOOLEAN:
255 case Opcodes.T_BYTE:
256 return OpCodes.Stelem_I1;
257 case Opcodes.T_CHAR:
258 case Opcodes.T_SHORT:
259 return OpCodes.Stelem_I2;
260 case Opcodes.T_INT:
261 return OpCodes.Stelem_I4;
262 case Opcodes.T_LONG:
263 return OpCodes.Stelem_I8;
264 case Opcodes.T_FLOAT:
265 return OpCodes.Stelem_R4;
266 case Opcodes.T_DOUBLE:
267 return OpCodes.Stelem_R8;
268 case 0:
269 return OpCodes.Stelem_Ref;
270 default:
271 throw new BailoutException(Bailout.PreconditionViolated, "tcode = " + tcode);
272 }
273 }
274
283 private void emitImplicitConversion(BasicType ptype, Class pclass, object arg)
284 {
285 //assert(basicType(pclass) == ptype); // boxing/unboxing handled by caller
286 if (pclass == ptype.basicTypeClass() && ptype != BasicType.L_TYPE)
287 return; // nothing to do
288 switch (ptype.name())
289 {
290 case "L_TYPE":
291 if (VerifyType.isNullConversion(context.JavaBase.TypeOfJavaLangObject.ClassObject, pclass, false))
292 {
293 //if (PROFILE_LEVEL > 0)
294 // emitReferenceCast(Object.class, arg);
295 return;
296 }
297 emitReferenceCast(pclass, arg);
298 return;
299 case "I_TYPE":
300 if (!VerifyType.isNullConversion(java.lang.Integer.TYPE, pclass, false))
301 emitPrimCast(ptype.basicTypeWrapper(), Wrapper.forPrimitiveType(pclass));
302 return;
303 }
304 throw new BailoutException(Bailout.PreconditionViolated, "bad implicit conversion: tc=" + ptype + ": " + pclass);
305 }
306
308 private void assertStaticType(Class cls, Name n)
309 {
310 // [IKVM] not implemented
311 }
312
313 void emitReferenceCast(Class cls, object arg)
314 {
315 // [IKVM] handle the type system hole that is caused by arrays being both derived from cli.System.Array and directly from java.lang.Object
316 if (cls != context.JavaBase.TypeOfCliSystemObject.ClassObject)
317 {
318 RuntimeJavaType.FromClass(cls).EmitCheckcast(ilgen);
319 }
320 }
321
322 sealed class AnonymousClass : RuntimeJavaType
323 {
324
329 public AnonymousClass(RuntimeContext context) :
330 base(context, TypeFlags.Anonymous, Modifiers.Super | Modifiers.Final, "java.lang.invoke.LambdaForm$MH")
331 {
332
333 }
334
335 internal override RuntimeClassLoader ClassLoader => Context.ClassLoaderFactory.GetBootstrapClassLoader();
336
337 internal override Type TypeAsTBD
338 {
339 get { throw new InvalidOperationException(); }
340 }
341
342 internal override RuntimeJavaType BaseTypeWrapper
343 {
344 get { return Context.JavaBase.TypeOfJavaLangObject; }
345 }
346 }
347
351 public static global::java.lang.invoke.MemberName generateCustomizedCode(java.lang.invoke.LambdaForm form, java.lang.invoke.MethodType invokerType)
352 {
353 try
354 {
355 java.lang.invoke.MemberName memberName = new java.lang.invoke.MemberName();
356 memberName._clazz(JVM.Context.GetOrCreateSingleton(() => new AnonymousClass(JVM.Context)).ClassObject);
357 memberName._name(form.debugName);
358 memberName._type(invokerType);
359 memberName._flags(MethodHandleNatives.Constants.MN_IS_METHOD | MethodHandleNatives.Constants.ACC_STATIC | (MethodHandleNatives.Constants.REF_invokeStatic << MethodHandleNatives.Constants.MN_REFERENCE_KIND_SHIFT));
360 memberName.vmtarget = new NativeInvokerBytecodeGenerator(JVM.Context, form, invokerType).generateCustomizedCodeBytes();
361 return memberName;
362 }
363#if DEBUG
364 catch (BailoutException x)
365 {
366 Console.WriteLine(x.Message);
367 Console.WriteLine("generateCustomizedCode: " + form + ", " + invokerType);
368 }
369#else
370 catch (BailoutException)
371 {
372 }
373#endif
374 return InvokerBytecodeGenerator.generateCustomizedCode(form, invokerType);
375 }
376
380 private Delegate generateCustomizedCodeBytes()
381 {
382 // iterate over the form's names, generating bytecode instructions for each
383 // start iterating at the first name following the arguments
384 Name onStack = null;
385 for (int i = lambdaForm._arity(); i < lambdaForm.names.Length; i++)
386 {
387 Name name = lambdaForm.names[i];
388
389 emitStoreResult(onStack);
390 onStack = name; // unless otherwise modified below
391 java.lang.invoke.MethodHandleImpl.Intrinsic intr = name.function.intrinsicName();
392 switch (intr.name())
393 {
394 case "SELECT_ALTERNATIVE":
395 //assert isSelectAlternative(i);
396 onStack = emitSelectAlternative(name, lambdaForm.names[i + 1]);
397 i++; // skip MH.invokeBasic of the selectAlternative result
398 continue;
399 case "GUARD_WITH_CATCH":
400 //assert isGuardWithCatch(i);
401 onStack = emitGuardWithCatch(i);
402 i = i + 2; // Jump to the end of GWC idiom
403 continue;
404 case "NEW_ARRAY":
405 Class rtype = name.function.methodType().returnType();
406 if (InvokerBytecodeGenerator.isStaticallyNameable(rtype))
407 {
408 emitNewArray(name);
409 continue;
410 }
411 break;
412 case "ARRAY_LOAD":
413 emitArrayLoad(name);
414 continue;
415 case "IDENTITY":
416 //assert(name.arguments.length == 1);
417 emitPushArguments(name);
418 continue;
419 case "NONE":
420 // no intrinsic associated
421 break;
422 // [IKVM] ARRAY_STORE and ZERO appear to be unused
423 default:
424 throw new BailoutException(Bailout.UnsupportedIntrinsic, "Unknown intrinsic: " + intr);
425 }
426
427 java.lang.invoke.MemberName member = name.function._member();
428 if (isStaticallyInvocable(member))
429 {
430 emitStaticInvoke(member, name);
431 }
432 else
433 {
434 emitInvoke(name);
435 }
436 }
437
438 // return statement
439 emitReturn(onStack);
440
441 ilgen.DoEmit();
442 return dm.CreateDelegate(delegateType, constants.ToArray());
443 }
444
445 void emitArrayLoad(Name name)
446 {
447 OpCode arrayOpcode = OpCodes.Ldelem_Ref;
448 Class elementType = name.function.methodType().parameterType(0).getComponentType();
449 emitPushArguments(name);
450 if (elementType.isPrimitive())
451 {
452 Wrapper w = Wrapper.forPrimitiveType(elementType);
453 arrayOpcode = arrayLoadOpcode(arrayTypeCode(w));
454 }
455 ilgen.Emit(arrayOpcode);
456 }
457
461 void emitInvoke(Name name)
462 {
463 //assert(!isLinkerMethodInvoke(name)); // should use the static path for these
464 if (true)
465 {
466 // push receiver
467 java.lang.invoke.MethodHandle target = name.function._resolvedHandle();
468 //assert(target != null) : name.exprString();
469 //mv.visitLdcInsn(constantPlaceholder(target));
470 EmitConstant(target);
471 emitReferenceCast(context.JavaBase.TypeOfJavaLangInvokeMethodHandle.ClassObject, target);
472 }
473 else
474 {
475 // load receiver
476 //emitAloadInsn(0);
477 //emitReferenceCast(MethodHandle.class, null);
478 //mv.visitFieldInsn(Opcodes.GETFIELD, MH, "form", LF_SIG);
479 //mv.visitFieldInsn(Opcodes.GETFIELD, LF, "names", LFN_SIG);
480 // TODO more to come
481 }
482
483 // push arguments
484 emitPushArguments(name);
485
486 // invocation
487 java.lang.invoke.MethodType type = name.function.methodType();
488 //mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", type.basicType().toMethodDescriptorString(), false);
489 EmitInvokeBasic(type.basicType());
490 }
491
492 bool isStaticallyInvocable(java.lang.invoke.MemberName member)
493 {
494 if (member == null) return false;
495 if (member.isConstructor()) return false;
496 Class cls = member.getDeclaringClass();
497 if (cls.isArray() || cls.isPrimitive())
498 return false; // FIXME
499
500 /*
501 if (cls.isAnonymousClass() || cls.isLocalClass())
502 return false; // inner class of some sort
503 if (cls.getClassLoader() != MethodHandle.class.getClassLoader())
504 return false; // not on BCP
505 if (ReflectUtil.isVMAnonymousClass(cls)) // FIXME: switch to supported API once it is added
506 return false;
507 MethodType mtype = member.getMethodOrFieldType();
508 if (!isStaticallyNameable(mtype.returnType()))
509 return false;
510 for (Class<?> ptype : mtype.parameterArray())
511 if (!isStaticallyNameable(ptype))
512 return false;
513 if (!member.isPrivate() && VerifyAccess.isSamePackage(MethodHandle.class, cls))
514 return true; // in java.lang.invoke package
515 if (member.isPublic() && isStaticallyNameable(cls))
516 return true;
517 */
518
519 if (member.isMethod())
520 {
521 // [IKVM] If we can't call the method directly, invoke it via the invokeBasic infrastructure.
522 return IsMethodHandleLinkTo(member)
523 || IsMethodHandleInvokeBasic(member)
524 || IsStaticallyInvocable(GetMethodWrapper(member));
525 }
526
527 if (member.isField())
528 {
529 // [IKVM] If we can't access the field directly, use the invokeBasic infrastructure.
530 return IsStaticallyInvocable(GetFieldWrapper(member));
531 }
532
533 return false;
534 }
535
536 /*
537 static boolean isStaticallyNameable(Class<?> cls) {
538 if (cls == Object.class)
539 return true;
540 while (cls.isArray())
541 cls = cls.getComponentType();
542 if (cls.isPrimitive())
543 return true; // int[].class, for example
544 if (ReflectUtil.isVMAnonymousClass(cls)) // FIXME: switch to supported API once it is added
545 return false;
546 // could use VerifyAccess.isClassAccessible but the following is a safe approximation
547 if (cls.getClassLoader() != Object.class.getClassLoader())
548 return false;
549 if (VerifyAccess.isSamePackage(MethodHandle.class, cls))
550 return true;
551 if (!Modifier.isPublic(cls.getModifiers()))
552 return false;
553 for (Class<?> pkgcls : STATICALLY_INVOCABLE_PACKAGES) {
554 if (VerifyAccess.isSamePackage(pkgcls, cls))
555 return true;
556 }
557 return false;
558 }
559 */
560
561 void emitStaticInvoke(Name name)
562 {
563 emitStaticInvoke(name.function._member(), name);
564 }
565
569 void emitStaticInvoke(java.lang.invoke.MemberName member, Name name)
570 {
571 // push arguments
572 emitPushArguments(name);
573
574 // invocation
575 if (member.isMethod())
576 {
577 if (IsMethodHandleLinkTo(member))
578 {
579 java.lang.invoke.MethodType mt = member.getMethodType();
580 RuntimeJavaType[] args = new RuntimeJavaType[mt.parameterCount()];
581 for (int j = 0; j < args.Length; j++)
582 {
583 args[j] = RuntimeJavaType.FromClass(mt.parameterType(j));
584 args[j].Finish();
585 }
586 RuntimeJavaType ret = RuntimeJavaType.FromClass(mt.returnType());
587 ret.Finish();
588 Compiler.MethodHandleMethodWrapper.EmitLinkToCall(context, ilgen, args, ret);
589 ret.EmitConvSignatureTypeToStackType(ilgen);
590 }
591 else if (IsMethodHandleInvokeBasic(member))
592 {
593 EmitInvokeBasic(member.getMethodType());
594 }
595 else
596 {
597 switch (member.getReferenceKind())
598 {
599 case MethodHandleNatives.Constants.REF_invokeInterface:
600 case MethodHandleNatives.Constants.REF_invokeSpecial:
601 case MethodHandleNatives.Constants.REF_invokeStatic:
602 case MethodHandleNatives.Constants.REF_invokeVirtual:
603 break;
604 default:
605 throw new BailoutException(Bailout.UnsupportedRefKind, member);
606 }
607 RuntimeJavaMethod mw = GetMethodWrapper(member);
608 if (!IsStaticallyInvocable(mw))
609 {
610 throw new BailoutException(Bailout.NotStaticallyInvocable, member);
611 }
612 mw.Link();
613 mw.DeclaringType.Finish();
614 mw.ResolveMethod();
615 if (mw.HasCallerID)
616 {
617 EmitConstant(DynamicCallerIDProvider.Instance);
618 ilgen.Emit(OpCodes.Call, context.ByteCodeHelperMethods.DynamicCallerID);
619 }
620 if (mw.IsStatic || member.getReferenceKind() == MethodHandleNatives.Constants.REF_invokeSpecial)
621 {
622 mw.EmitCall(ilgen);
623 }
624 else
625 {
626 mw.EmitCallvirt(ilgen);
627 }
628 mw.ReturnType.EmitConvSignatureTypeToStackType(ilgen);
629 }
630 }
631 else if (member.isField())
632 {
633 RuntimeJavaField fw = GetFieldWrapper(member);
634 if (!IsStaticallyInvocable(fw))
635 {
636 throw new BailoutException(Bailout.NotStaticallyInvocable, member);
637 }
638 fw.Link();
639 fw.DeclaringType.Finish();
640 fw.ResolveField();
641 switch (member.getReferenceKind())
642 {
643 case MethodHandleNatives.Constants.REF_getField:
644 case MethodHandleNatives.Constants.REF_getStatic:
645 fw.EmitGet(ilgen);
646 fw.FieldTypeWrapper.EmitConvSignatureTypeToStackType(ilgen);
647 break;
648 case MethodHandleNatives.Constants.REF_putField:
649 case MethodHandleNatives.Constants.REF_putStatic:
650 fw.EmitSet(ilgen);
651 break;
652 default:
653 throw new BailoutException(Bailout.UnsupportedRefKind, member);
654 }
655 }
656 else
657 {
658 throw new BailoutException(Bailout.NotStaticallyInvocable, member);
659 }
660 }
661
662 void emitNewArray(Name name)
663 {
664 Class rtype = name.function.methodType().returnType();
665 if (name.arguments.Length == 0)
666 {
667 // The array will be a constant.
668 object emptyArray;
669 try
670 {
671 emptyArray = name.function._resolvedHandle().invoke();
672 }
673 catch (Exception ex)
674 {
675 throw new java.lang.InternalError(ex);
676 }
677 //assert(java.lang.reflect.Array.getLength(emptyArray) == 0);
678 //assert(emptyArray.getClass() == rtype); // exact typing
679 //mv.visitLdcInsn(constantPlaceholder(emptyArray));
680 EmitConstant(emptyArray);
681 emitReferenceCast(rtype, emptyArray);
682 return;
683 }
684 Class arrayElementType = rtype.getComponentType();
685 //assert(arrayElementType != null);
686 emitIconstInsn(name.arguments.Length);
687 OpCode xas = OpCodes.Stelem_Ref;
688 if (!arrayElementType.isPrimitive())
689 {
690 RuntimeJavaType tw = RuntimeJavaType.FromClass(arrayElementType);
691 if (tw.IsUnloadable || tw.IsGhost || tw.IsGhostArray || tw.IsNonPrimitiveValueType)
692 {
693 throw new BailoutException(Bailout.UnsupportedArrayType, tw);
694 }
695 ilgen.Emit(OpCodes.Newarr, tw.TypeAsArrayType);
696 }
697 else
698 {
699 byte tc = arrayTypeCode(Wrapper.forPrimitiveType(arrayElementType));
700 xas = arrayInsnOpcode(tc);
701 //mv.visitIntInsn(Opcodes.NEWARRAY, tc);
702 ilgen.Emit(OpCodes.Newarr, RuntimeJavaType.FromClass(arrayElementType).TypeAsArrayType);
703 }
704 // store arguments
705 for (int i = 0; i < name.arguments.Length; i++)
706 {
707 //mv.visitInsn(Opcodes.DUP);
708 ilgen.Emit(OpCodes.Dup);
709 emitIconstInsn(i);
710 emitPushArgument(name, i);
711 //mv.visitInsn(xas);
712 ilgen.Emit(xas);
713 }
714 // the array is left on the stack
715 assertStaticType(rtype, name);
716 }
717
729 private Name emitSelectAlternative(Name selectAlternativeName, Name invokeBasicName)
730 {
731 //assert isStaticallyInvocable(invokeBasicName);
732
733 Name receiver = (Name)invokeBasicName.arguments[0];
734
735 CodeEmitterLabel L_fallback = ilgen.DefineLabel();
736 CodeEmitterLabel L_done = ilgen.DefineLabel();
737
738 // load test result
739 emitPushArgument(selectAlternativeName, 0);
740
741 // if_icmpne L_fallback
742 ilgen.EmitBrfalse(L_fallback);
743
744 // invoke selectAlternativeName.arguments[1]
745 //Class<?>[] preForkClasses = localClasses.clone();
746 emitPushArgument(selectAlternativeName, 1); // get 2nd argument of selectAlternative
747 emitAstoreInsn(receiver.index()); // store the MH in the receiver slot
748 emitStaticInvoke(invokeBasicName);
749
750 // goto L_done
751 ilgen.EmitBr(L_done);
752
753 // L_fallback:
754 ilgen.MarkLabel(L_fallback);
755
756 // invoke selectAlternativeName.arguments[2]
757 //System.arraycopy(preForkClasses, 0, localClasses, 0, preForkClasses.length);
758 emitPushArgument(selectAlternativeName, 2); // get 3rd argument of selectAlternative
759 emitAstoreInsn(receiver.index()); // store the MH in the receiver slot
760 emitStaticInvoke(invokeBasicName);
761
762 // L_done:
763 ilgen.MarkLabel(L_done);
764 // for now do not bother to merge typestate; just reset to the dominator state
765 //System.arraycopy(preForkClasses, 0, localClasses, 0, preForkClasses.length);
766
767 return invokeBasicName; // return what's on stack
768 }
769
790 private Name emitGuardWithCatch(int pos)
791 {
792 Name args = lambdaForm.names[pos];
793 Name invoker = lambdaForm.names[pos + 1];
794 Name result = lambdaForm.names[pos + 2];
795
796 CodeEmitterLabel L_handler = ilgen.DefineLabel();
797 CodeEmitterLabel L_done = ilgen.DefineLabel();
798
799 Class returnType = result.function._resolvedHandle().type().returnType();
800 java.lang.invoke.MethodType type = args.function._resolvedHandle().type()
801 .dropParameterTypes(0, 1)
802 .changeReturnType(returnType);
803
804 // Normal case
805 ilgen.BeginExceptionBlock();
806 // load target
807 emitPushArgument(invoker, 0);
808 emitPushArguments(args, 1); // skip 1st argument: method handle
809 EmitInvokeBasic(type.basicType());
810 CodeEmitterLocal returnValue = null;
811 if (returnType != java.lang.Void.TYPE)
812 {
813 returnValue = ilgen.DeclareLocal(RuntimeJavaType.FromClass(returnType).TypeAsLocalOrStackType);
814 ilgen.Emit(OpCodes.Stloc, returnValue);
815 }
816 ilgen.EmitLeave(L_done);
817
818 // Exceptional case
819 ilgen.BeginCatchBlock(typeof(Exception));
820
821 // [IKVM] map the exception and store it in a local and exit the handler
822 ilgen.EmitLdc_I4(0);
823 ilgen.Emit(OpCodes.Call, context.ByteCodeHelperMethods.MapException.MakeGenericMethod(typeof(Exception)));
824 CodeEmitterLocal exception = ilgen.DeclareLocal(typeof(Exception));
825 ilgen.Emit(OpCodes.Stloc, exception);
826 ilgen.EmitLeave(L_handler);
827 ilgen.EndExceptionBlock();
828
829 // Check exception's type
830 ilgen.MarkLabel(L_handler);
831 // load exception class
832 emitPushArgument(invoker, 1);
833 ilgen.Emit(OpCodes.Ldloc, exception);
834 context.JavaBase.TypeOfJavaLangClass.GetMethod("isInstance", "(Ljava.lang.Object;)Z", false).EmitCall(ilgen);
835 CodeEmitterLabel L_rethrow = ilgen.DefineLabel();
836 ilgen.EmitBrfalse(L_rethrow);
837
838 // Invoke catcher
839 // load catcher
840 emitPushArgument(invoker, 2);
841 ilgen.Emit(OpCodes.Ldloc, exception);
842 emitPushArguments(args, 1); // skip 1st argument: method handle
843 MethodType catcherType = type.insertParameterTypes(0, context.JavaBase.TypeOfjavaLangThrowable.ClassObject);
844 EmitInvokeBasic(catcherType.basicType());
845 if (returnValue != null)
846 {
847 ilgen.Emit(OpCodes.Stloc, returnValue);
848 }
849 ilgen.EmitBr(L_done);
850
851 ilgen.MarkLabel(L_rethrow);
852 ilgen.Emit(OpCodes.Ldloc, exception);
853 ilgen.Emit(OpCodes.Call, context.CompilerFactory.UnmapExceptionMethod);
854 ilgen.Emit(OpCodes.Throw);
855
856 ilgen.MarkLabel(L_done);
857 if (returnValue != null)
858 {
859 ilgen.Emit(OpCodes.Ldloc, returnValue);
860 }
861
862 return result;
863 }
864
865 private void emitPushArguments(Name args)
866 {
867 emitPushArguments(args, 0);
868 }
869
870 private void emitPushArguments(Name args, int start)
871 {
872 for (int i = start; i < args.arguments.Length; i++)
873 {
874 emitPushArgument(args, i);
875 }
876 }
877
878 private void emitPushArgument(Name name, int paramIndex)
879 {
880 object arg = name.arguments[paramIndex];
881 Class ptype = name.function.methodType().parameterType(paramIndex);
882 emitPushArgument(ptype, arg);
883 }
884
885 private void emitPushArgument(Class ptype, object arg)
886 {
887 BasicType bptype = BasicType.basicType(ptype);
888 if (arg is Name)
889 {
890 Name n = (Name)arg;
891 emitLoadInsn(n._type(), n.index());
892 emitImplicitConversion(n._type(), ptype, n);
893 }
894 else if ((arg == null || arg is string) && bptype == BasicType.L_TYPE)
895 {
896 emitConst(arg);
897 }
898 else
899 {
900 if (Wrapper.isWrapperType(ikvm.extensions.ExtensionMethods.getClass(arg)) && bptype != BasicType.L_TYPE)
901 {
902 emitConst(arg);
903 }
904 else
905 {
906 EmitConstant(arg);
907 emitImplicitConversion(BasicType.L_TYPE, ptype, arg);
908 }
909 }
910 }
911
915 private void emitStoreResult(Name name)
916 {
917 if (name != null && name._type() != BasicType.V_TYPE)
918 {
919 // non-void: actually assign
920 emitStoreInsn(name._type(), name.index());
921 }
922 }
923
927 private void emitReturn(Name onStack)
928 {
929 // return statement
930 Class rclass = invokerType.returnType();
931 BasicType rtype = lambdaForm.returnType();
932 //assert(rtype == basicType(rclass)); // must agree
933 if (rtype == BasicType.V_TYPE)
934 {
935 // [IKVM] unlike the JVM, the CLR doesn't like left over values on the stack
936 if (onStack != null && onStack._type() != BasicType.V_TYPE)
937 {
938 ilgen.Emit(OpCodes.Pop);
939 }
940 }
941 else
942 {
943 LambdaForm.Name rn = lambdaForm.names[lambdaForm.result];
944
945 // put return value on the stack if it is not already there
946 if (rn != onStack)
947 {
948 emitLoadInsn(rtype, lambdaForm.result);
949 }
950
951 emitImplicitConversion(rtype, rclass, rn);
952 }
953 ilgen.Emit(OpCodes.Ret);
954 }
955
959 private void emitPrimCast(Wrapper from, Wrapper to)
960 {
961 // Here's how.
962 // - indicates forbidden
963 // <-> indicates implicit
964 // to ----> boolean byte short char int long float double
965 // from boolean <-> - - - - - - -
966 // byte - <-> i2s i2c <-> i2l i2f i2d
967 // short - i2b <-> i2c <-> i2l i2f i2d
968 // char - i2b i2s <-> <-> i2l i2f i2d
969 // int - i2b i2s i2c <-> i2l i2f i2d
970 // long - l2i,i2b l2i,i2s l2i,i2c l2i <-> l2f l2d
971 // float - f2i,i2b f2i,i2s f2i,i2c f2i f2l <-> f2d
972 // double - d2i,i2b d2i,i2s d2i,i2c d2i d2l d2f <->
973 if (from == to)
974 {
975 // no cast required, should be dead code anyway
976 return;
977 }
978 if (from.isSubwordOrInt())
979 {
980 // cast from {byte,short,char,int} to anything
981 emitI2X(to);
982 }
983 else
984 {
985 // cast from {long,float,double} to anything
986 if (to.isSubwordOrInt())
987 {
988 // cast to {byte,short,char,int}
989 emitX2I(from);
990 if (to.bitWidth() < 32)
991 {
992 // targets other than int require another conversion
993 emitI2X(to);
994 }
995 }
996 else
997 {
998 // cast to {long,float,double} - this is verbose
999 bool error = false;
1000 switch (from.name())
1001 {
1002 case "LONG":
1003 switch (to.name())
1004 {
1005 case "FLOAT": ilgen.Emit(OpCodes.Conv_R4); break;
1006 case "DOUBLE": ilgen.Emit(OpCodes.Conv_R8); break;
1007 default: error = true; break;
1008 }
1009 break;
1010 case "FLOAT":
1011 switch (to.name())
1012 {
1013 case "LONG": ilgen.Emit(OpCodes.Call, context.ByteCodeHelperMethods.f2l); break;
1014 case "DOUBLE": ilgen.Emit(OpCodes.Conv_R8); break;
1015 default: error = true; break;
1016 }
1017 break;
1018 case "DOUBLE":
1019 switch (to.name())
1020 {
1021 case "LONG": ilgen.Emit(OpCodes.Call, context.ByteCodeHelperMethods.d2l); break;
1022 case "FLOAT": ilgen.Emit(OpCodes.Conv_R4); break;
1023 default: error = true; break;
1024 }
1025 break;
1026 default:
1027 error = true;
1028 break;
1029 }
1030 if (error)
1031 {
1032 throw new BailoutException(Bailout.PreconditionViolated, "unhandled prim cast: " + from + "2" + to);
1033 }
1034 }
1035 }
1036 }
1037
1038 private void emitI2X(Wrapper type)
1039 {
1040 switch (type.name())
1041 {
1042 case "BYTE": ilgen.Emit(OpCodes.Conv_I1); break;
1043 case "SHORT": ilgen.Emit(OpCodes.Conv_I2); break;
1044 case "CHAR": ilgen.Emit(OpCodes.Conv_U2); break;
1045 case "INT": /* naught */ break;
1046 case "LONG": ilgen.Emit(OpCodes.Conv_I8); break;
1047 case "FLOAT": ilgen.Emit(OpCodes.Conv_R4); break;
1048 case "DOUBLE": ilgen.Emit(OpCodes.Conv_R8); break;
1049 case "BOOLEAN":
1050 // For compatibility with ValueConversions and explicitCastArguments:
1051 ilgen.EmitLdc_I4(1);
1052 ilgen.Emit(OpCodes.And);
1053 break;
1054 default: throw new BailoutException(Bailout.PreconditionViolated, "unknown type: " + type);
1055 }
1056 }
1057
1058 private void emitX2I(Wrapper type)
1059 {
1060 switch (type.name())
1061 {
1062 case "LONG": ilgen.Emit(OpCodes.Conv_I4); break;
1063 case "FLOAT": ilgen.Emit(OpCodes.Call, context.ByteCodeHelperMethods.f2i); break;
1064 case "DOUBLE": ilgen.Emit(OpCodes.Call, context.ByteCodeHelperMethods.d2i); break;
1065 default: throw new BailoutException(Bailout.PreconditionViolated, "unknown type: " + type);
1066 }
1067 }
1068
1069 private void EmitConstant(object obj)
1070 {
1071 if (obj == null)
1072 {
1073 ilgen.Emit(OpCodes.Ldnull);
1074 return;
1075 }
1076 int index = constants.IndexOf(obj);
1077 if (index == -1)
1078 {
1079 index = constants.Count;
1080 constants.Add(obj);
1081 }
1082 ilgen.EmitLdarg(0); // we want the bound value, not the real first parameter
1083 ilgen.EmitLdc_I4(index);
1084 ilgen.Emit(OpCodes.Ldelem_Ref);
1085 }
1086
1087 private void EmitInvokeBasic(global::java.lang.invoke.MethodType mt)
1088 {
1089 RuntimeJavaType[] args = new RuntimeJavaType[mt.parameterCount()];
1090 for (int i = 0; i < args.Length; i++)
1091 {
1092 args[i] = RuntimeJavaType.FromClass(mt.parameterType(i));
1093 args[i].Finish();
1094 }
1095 RuntimeJavaType ret = RuntimeJavaType.FromClass(mt.returnType());
1096 ret.Finish();
1097 Compiler.MethodHandleMethodWrapper.EmitInvokeBasic(JVM.Context, ilgen, args, ret, false);
1098 }
1099
1100 private OpCode arrayLoadOpcode(byte tcode)
1101 {
1102 switch (tcode)
1103 {
1104 case Opcodes.T_BOOLEAN:
1105 case Opcodes.T_BYTE:
1106 return OpCodes.Ldelem_I1;
1107 case Opcodes.T_CHAR:
1108 return OpCodes.Ldelem_U2;
1109 case Opcodes.T_SHORT:
1110 return OpCodes.Ldelem_I2;
1111 case Opcodes.T_INT:
1112 return OpCodes.Ldelem_I4;
1113 case Opcodes.T_LONG:
1114 return OpCodes.Ldelem_I8;
1115 case Opcodes.T_FLOAT:
1116 return OpCodes.Ldelem_R4;
1117 case Opcodes.T_DOUBLE:
1118 return OpCodes.Ldelem_R8;
1119 case 0:
1120 return OpCodes.Ldelem_Ref;
1121 default:
1122 throw new BailoutException(Bailout.PreconditionViolated, "tcode = " + tcode);
1123 }
1124 }
1125
1126 private bool IsMethodHandleLinkTo(java.lang.invoke.MemberName member)
1127 {
1128 return member.getDeclaringClass() == context.JavaBase.TypeOfJavaLangInvokeMethodHandle.ClassObject && member.getName().StartsWith("linkTo", StringComparison.Ordinal);
1129 }
1130
1131 private bool IsMethodHandleInvokeBasic(java.lang.invoke.MemberName member)
1132 {
1133 return member.getDeclaringClass() == context.JavaBase.TypeOfJavaLangInvokeMethodHandle.ClassObject && member.getName() == "invokeBasic";
1134 }
1135
1136 private RuntimeJavaMethod GetMethodWrapper(java.lang.invoke.MemberName member)
1137 {
1138 return RuntimeJavaType.FromClass(member.getDeclaringClass()).GetMethod(member.getName(), member.getSignature().Replace('/', '.'), true);
1139 }
1140
1141 private bool IsStaticallyInvocable(RuntimeJavaMethod mw)
1142 {
1143 if (mw == null || mw.DeclaringType.IsUnloadable || mw.DeclaringType.IsGhost || mw.DeclaringType.IsNonPrimitiveValueType || mw.IsFinalizeOrClone || mw.IsDynamicOnly)
1144 {
1145 return false;
1146 }
1147 if (mw.ReturnType.IsUnloadable || mw.ReturnType.IsGhost || mw.ReturnType.IsNonPrimitiveValueType)
1148 {
1149 return false;
1150 }
1151 foreach (RuntimeJavaType tw in mw.GetParameters())
1152 {
1153 if (tw.IsUnloadable || tw.IsGhost || tw.IsNonPrimitiveValueType)
1154 {
1155 return false;
1156 }
1157 }
1158 return true;
1159 }
1160
1161 private RuntimeJavaField GetFieldWrapper(java.lang.invoke.MemberName member)
1162 {
1163 return RuntimeJavaType.FromClass(member.getDeclaringClass()).GetFieldWrapper(member.getName(), member.getSignature().Replace('/', '.'));
1164 }
1165
1166 private bool IsStaticallyInvocable(RuntimeJavaField fw)
1167 {
1168 return fw != null
1169 && !fw.FieldTypeWrapper.IsUnloadable
1170 && !fw.FieldTypeWrapper.IsGhost
1171 && !fw.FieldTypeWrapper.IsNonPrimitiveValueType;
1172 }
1173
1174#endif
1175
1176 }
1177
1178}
IKVM.Reflection.Module Module
IKVM.Reflection.Type Type
global::sun.invoke.util.Wrapper Wrapper
global::java.lang.invoke.LambdaForm.BasicType BasicType
global::sun.invoke.util.VerifyType VerifyType
global::java.lang.Class Class
jdk. @internal.org.objectweb.asm.Opcodes Opcodes
global::java.lang.invoke.LambdaForm.Name Name
Main state of the running JVM.
Runtime support for a class loader.
Maintains services relevant to an instane of the IKVM runtime.
CoreClasses JavaBase
Gets the CoreClasses associated with this instance of the runtime.
static global::java.lang.invoke.MemberName generateCustomizedCode(java.lang.invoke.LambdaForm form, java.lang.invoke.MethodType invokerType)