64 readonly java.lang.invoke.LambdaForm lambdaForm;
65 readonly java.lang.invoke.MethodType invokerType;
66 readonly
Type delegateType;
67 readonly DynamicMethod dm;
69 readonly
int packedArgPos;
70 readonly
Type packedArgType;
72 readonly List<object> constants =
new List<object>();
81 NotStaticallyInvocable,
85 private sealed class BailoutException : Exception
88 internal BailoutException(Bailout reason,
object data)
89 : base(
"BAILOUT " + reason +
": " + data)
106 this.context = context ??
throw new ArgumentNullException(nameof(context));
107 this.lambdaForm = lambdaForm;
108 this.invokerType = invokerType;
110 if (invokerType != invokerType.basicType())
111 throw new BailoutException(Bailout.NotBasicType, invokerType);
113 this.delegateType = context.MethodHandleUtil.GetMemberWrapperDelegateType(invokerType);
114 var mi = context.MethodHandleUtil.GetDelegateInvokeMethod(delegateType);
118 this.dm =
new DynamicMethod(lambdaForm.debugName, mi.ReturnType, paramTypes, typeof(
object).
Module,
true);
119 this.ilgen = context.CodeEmitterFactory.Create(this.dm);
122 this.packedArgType = paramTypes[paramTypes.Length - 1];
123 this.packedArgPos = paramTypes.Length - 1;
127 this.packedArgPos = Int32.MaxValue;
131 for (
int i = lambdaForm._arity(); i < lambdaForm.names.Length; i++)
133 Name name = lambdaForm.names[i];
134 if (name.index() != i)
136 throw new BailoutException(Bailout.PreconditionViolated,
"name.index() != i");
138 switch (name.typeChar())
141 locals[i] = ilgen.DeclareLocal(context.Types.Object);
144 locals[i] = ilgen.DeclareLocal(context.Types.Int32);
147 locals[i] = ilgen.DeclareLocal(context.Types.Int64);
150 locals[i] = ilgen.DeclareLocal(context.Types.Single);
153 locals[i] = ilgen.DeclareLocal(context.Types.Double);
158 throw new BailoutException(Bailout.PreconditionViolated,
"Unsupported typeChar(): " + name.typeChar());
166 private void emitConst(
object con)
170 ilgen.Emit(OpCodes.Ldnull);
172 else if (con is
string)
174 ilgen.Emit(OpCodes.Ldstr, (
string)con);
178 ilgen.EmitLdc_I4(((
java.
lang.Integer)con).intValue());
182 ilgen.EmitLdc_I8(((
java.
lang.Long)con).longValue());
186 ilgen.EmitLdc_R4(((
java.
lang.Float)con).floatValue());
190 ilgen.EmitLdc_R8(((
java.
lang.Double)con).doubleValue());
194 ilgen.EmitLdc_I4(((
java.
lang.Boolean)con).booleanValue() ? 1 : 0);
198 throw new BailoutException(Bailout.UnsupportedConstant, con);
202 private void emitIconstInsn(
int i)
210 private void emitLoadInsn(
BasicType type,
int index)
213 if (locals[index] ==
null)
215 context.MethodHandleUtil.LoadPackedArg(ilgen, index, 1, packedArgPos, packedArgType);
219 ilgen.Emit(OpCodes.Ldloc, locals[index]);
223 private void emitStoreInsn(
BasicType type,
int index)
225 ilgen.Emit(OpCodes.Stloc, locals[index]);
228 private void emitAstoreInsn(
int index)
233 private byte arrayTypeCode(
Wrapper elementType)
235 switch (elementType.name())
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;
246 default:
throw new BailoutException(Bailout.PreconditionViolated,
"elemendType = " + elementType);
250 private OpCode arrayInsnOpcode(
byte tcode)
256 return OpCodes.Stelem_I1;
259 return OpCodes.Stelem_I2;
261 return OpCodes.Stelem_I4;
263 return OpCodes.Stelem_I8;
265 return OpCodes.Stelem_R4;
267 return OpCodes.Stelem_R8;
269 return OpCodes.Stelem_Ref;
271 throw new BailoutException(Bailout.PreconditionViolated,
"tcode = " + tcode);
283 private void emitImplicitConversion(
BasicType ptype,
Class pclass,
object arg)
286 if (pclass == ptype.basicTypeClass() && ptype !=
BasicType.L_TYPE)
288 switch (ptype.name())
291 if (
VerifyType.isNullConversion(context.JavaBase.TypeOfJavaLangObject.ClassObject, pclass,
false))
297 emitReferenceCast(pclass, arg);
301 emitPrimCast(ptype.basicTypeWrapper(),
Wrapper.forPrimitiveType(pclass));
304 throw new BailoutException(Bailout.PreconditionViolated,
"bad implicit conversion: tc=" + ptype +
": " + pclass);
308 private void assertStaticType(
Class cls,
Name n)
313 void emitReferenceCast(
Class cls,
object arg)
316 if (cls != context.JavaBase.TypeOfCliSystemObject.ClassObject)
318 RuntimeJavaType.FromClass(cls).EmitCheckcast(ilgen);
322 sealed class AnonymousClass : RuntimeJavaType
335 internal override RuntimeClassLoader ClassLoader => Context.ClassLoaderFactory.GetBootstrapClassLoader();
337 internal override Type TypeAsTBD
339 get {
throw new InvalidOperationException(); }
342 internal override RuntimeJavaType BaseTypeWrapper
344 get {
return Context.
JavaBase.TypeOfJavaLangObject; }
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));
364 catch (BailoutException x)
366 Console.WriteLine(x.Message);
367 Console.WriteLine(
"generateCustomizedCode: " + form +
", " + invokerType);
370 catch (BailoutException)
374 return InvokerBytecodeGenerator.generateCustomizedCode(form, invokerType);
380 private Delegate generateCustomizedCodeBytes()
385 for (
int i = lambdaForm._arity(); i < lambdaForm.names.Length; i++)
387 Name name = lambdaForm.names[i];
389 emitStoreResult(onStack);
391 java.lang.invoke.MethodHandleImpl.Intrinsic intr = name.function.intrinsicName();
394 case "SELECT_ALTERNATIVE":
396 onStack = emitSelectAlternative(name, lambdaForm.names[i + 1]);
399 case "GUARD_WITH_CATCH":
401 onStack = emitGuardWithCatch(i);
405 Class rtype = name.function.methodType().returnType();
406 if (InvokerBytecodeGenerator.isStaticallyNameable(rtype))
417 emitPushArguments(name);
424 throw new BailoutException(Bailout.UnsupportedIntrinsic,
"Unknown intrinsic: " + intr);
427 java.lang.invoke.MemberName member = name.function._member();
428 if (isStaticallyInvocable(member))
430 emitStaticInvoke(member, name);
442 return dm.CreateDelegate(delegateType, constants.ToArray());
445 void emitArrayLoad(
Name name)
447 OpCode arrayOpcode = OpCodes.Ldelem_Ref;
448 Class elementType = name.function.methodType().parameterType(0).getComponentType();
449 emitPushArguments(name);
450 if (elementType.isPrimitive())
453 arrayOpcode = arrayLoadOpcode(arrayTypeCode(w));
455 ilgen.Emit(arrayOpcode);
461 void emitInvoke(
Name name)
467 java.lang.invoke.MethodHandle target = name.function._resolvedHandle();
470 EmitConstant(target);
471 emitReferenceCast(context.JavaBase.TypeOfJavaLangInvokeMethodHandle.ClassObject, target);
484 emitPushArguments(name);
487 java.lang.invoke.MethodType type = name.function.methodType();
489 EmitInvokeBasic(type.basicType());
492 bool isStaticallyInvocable(
java.
lang.invoke.MemberName member)
494 if (member ==
null)
return false;
495 if (member.isConstructor())
return false;
496 Class cls = member.getDeclaringClass();
497 if (cls.isArray() || cls.isPrimitive())
519 if (member.isMethod())
522 return IsMethodHandleLinkTo(member)
523 || IsMethodHandleInvokeBasic(member)
524 || IsStaticallyInvocable(GetMethodWrapper(member));
527 if (member.isField())
530 return IsStaticallyInvocable(GetFieldWrapper(member));
561 void emitStaticInvoke(
Name name)
563 emitStaticInvoke(name.function._member(), name);
569 void emitStaticInvoke(
java.
lang.invoke.MemberName member,
Name name)
572 emitPushArguments(name);
575 if (member.isMethod())
577 if (IsMethodHandleLinkTo(member))
579 java.lang.invoke.MethodType mt = member.getMethodType();
580 RuntimeJavaType[] args =
new RuntimeJavaType[mt.parameterCount()];
581 for (
int j = 0; j < args.Length; j++)
583 args[j] = RuntimeJavaType.FromClass(mt.parameterType(j));
586 RuntimeJavaType ret = RuntimeJavaType.FromClass(mt.returnType());
588 Compiler.MethodHandleMethodWrapper.EmitLinkToCall(context, ilgen, args, ret);
589 ret.EmitConvSignatureTypeToStackType(ilgen);
591 else if (IsMethodHandleInvokeBasic(member))
593 EmitInvokeBasic(member.getMethodType());
597 switch (member.getReferenceKind())
599 case MethodHandleNatives.Constants.REF_invokeInterface:
600 case MethodHandleNatives.Constants.REF_invokeSpecial:
601 case MethodHandleNatives.Constants.REF_invokeStatic:
602 case MethodHandleNatives.Constants.REF_invokeVirtual:
605 throw new BailoutException(Bailout.UnsupportedRefKind, member);
607 RuntimeJavaMethod mw = GetMethodWrapper(member);
608 if (!IsStaticallyInvocable(mw))
610 throw new BailoutException(Bailout.NotStaticallyInvocable, member);
613 mw.DeclaringType.Finish();
617 EmitConstant(DynamicCallerIDProvider.Instance);
618 ilgen.Emit(OpCodes.Call, context.ByteCodeHelperMethods.DynamicCallerID);
620 if (mw.IsStatic || member.getReferenceKind() == MethodHandleNatives.Constants.REF_invokeSpecial)
626 mw.EmitCallvirt(ilgen);
628 mw.ReturnType.EmitConvSignatureTypeToStackType(ilgen);
631 else if (member.isField())
633 RuntimeJavaField fw = GetFieldWrapper(member);
634 if (!IsStaticallyInvocable(fw))
636 throw new BailoutException(Bailout.NotStaticallyInvocable, member);
639 fw.DeclaringType.Finish();
641 switch (member.getReferenceKind())
643 case MethodHandleNatives.Constants.REF_getField:
644 case MethodHandleNatives.Constants.REF_getStatic:
646 fw.FieldTypeWrapper.EmitConvSignatureTypeToStackType(ilgen);
648 case MethodHandleNatives.Constants.REF_putField:
649 case MethodHandleNatives.Constants.REF_putStatic:
653 throw new BailoutException(Bailout.UnsupportedRefKind, member);
658 throw new BailoutException(Bailout.NotStaticallyInvocable, member);
662 void emitNewArray(
Name name)
664 Class rtype = name.function.methodType().returnType();
665 if (name.arguments.Length == 0)
671 emptyArray = name.function._resolvedHandle().invoke();
675 throw new java.
lang.InternalError(ex);
680 EmitConstant(emptyArray);
681 emitReferenceCast(rtype, emptyArray);
684 Class arrayElementType = rtype.getComponentType();
686 emitIconstInsn(name.arguments.Length);
687 OpCode xas = OpCodes.Stelem_Ref;
688 if (!arrayElementType.isPrimitive())
690 RuntimeJavaType tw = RuntimeJavaType.FromClass(arrayElementType);
691 if (tw.IsUnloadable || tw.IsGhost || tw.IsGhostArray || tw.IsNonPrimitiveValueType)
693 throw new BailoutException(Bailout.UnsupportedArrayType, tw);
695 ilgen.Emit(OpCodes.Newarr, tw.TypeAsArrayType);
699 byte tc = arrayTypeCode(
Wrapper.forPrimitiveType(arrayElementType));
700 xas = arrayInsnOpcode(tc);
702 ilgen.Emit(OpCodes.Newarr, RuntimeJavaType.FromClass(arrayElementType).TypeAsArrayType);
705 for (
int i = 0; i < name.arguments.Length; i++)
708 ilgen.Emit(OpCodes.Dup);
710 emitPushArgument(name, i);
715 assertStaticType(rtype, name);
729 private Name emitSelectAlternative(
Name selectAlternativeName,
Name invokeBasicName)
733 Name receiver = (
Name)invokeBasicName.arguments[0];
735 CodeEmitterLabel L_fallback = ilgen.DefineLabel();
736 CodeEmitterLabel L_done = ilgen.DefineLabel();
739 emitPushArgument(selectAlternativeName, 0);
742 ilgen.EmitBrfalse(L_fallback);
746 emitPushArgument(selectAlternativeName, 1);
747 emitAstoreInsn(receiver.index());
748 emitStaticInvoke(invokeBasicName);
751 ilgen.EmitBr(L_done);
754 ilgen.MarkLabel(L_fallback);
758 emitPushArgument(selectAlternativeName, 2);
759 emitAstoreInsn(receiver.index());
760 emitStaticInvoke(invokeBasicName);
763 ilgen.MarkLabel(L_done);
767 return invokeBasicName;
790 private Name emitGuardWithCatch(
int pos)
792 Name args = lambdaForm.names[pos];
793 Name invoker = lambdaForm.names[pos + 1];
794 Name result = lambdaForm.names[pos + 2];
796 CodeEmitterLabel L_handler = ilgen.DefineLabel();
797 CodeEmitterLabel L_done = ilgen.DefineLabel();
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);
805 ilgen.BeginExceptionBlock();
807 emitPushArgument(invoker, 0);
808 emitPushArguments(args, 1);
809 EmitInvokeBasic(type.basicType());
810 CodeEmitterLocal returnValue =
null;
811 if (returnType !=
java.
lang.Void.TYPE)
813 returnValue = ilgen.DeclareLocal(RuntimeJavaType.FromClass(returnType).TypeAsLocalOrStackType);
814 ilgen.Emit(OpCodes.Stloc, returnValue);
816 ilgen.EmitLeave(L_done);
819 ilgen.BeginCatchBlock(typeof(Exception));
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();
830 ilgen.MarkLabel(L_handler);
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);
840 emitPushArgument(invoker, 2);
841 ilgen.Emit(OpCodes.Ldloc, exception);
842 emitPushArguments(args, 1);
843 MethodType catcherType = type.insertParameterTypes(0, context.JavaBase.TypeOfjavaLangThrowable.ClassObject);
844 EmitInvokeBasic(catcherType.basicType());
845 if (returnValue !=
null)
847 ilgen.Emit(OpCodes.Stloc, returnValue);
849 ilgen.EmitBr(L_done);
851 ilgen.MarkLabel(L_rethrow);
852 ilgen.Emit(OpCodes.Ldloc, exception);
853 ilgen.Emit(OpCodes.Call, context.CompilerFactory.UnmapExceptionMethod);
854 ilgen.Emit(OpCodes.Throw);
856 ilgen.MarkLabel(L_done);
857 if (returnValue !=
null)
859 ilgen.Emit(OpCodes.Ldloc, returnValue);
865 private void emitPushArguments(
Name args)
867 emitPushArguments(args, 0);
870 private void emitPushArguments(
Name args,
int start)
872 for (
int i = start; i < args.arguments.Length; i++)
874 emitPushArgument(args, i);
878 private void emitPushArgument(
Name name,
int paramIndex)
880 object arg = name.arguments[paramIndex];
881 Class ptype = name.function.methodType().parameterType(paramIndex);
882 emitPushArgument(ptype, arg);
885 private void emitPushArgument(
Class ptype,
object arg)
891 emitLoadInsn(n._type(), n.index());
892 emitImplicitConversion(n._type(), ptype, n);
894 else if ((arg ==
null || arg is
string) && bptype ==
BasicType.L_TYPE)
900 if (
Wrapper.isWrapperType(ikvm.extensions.ExtensionMethods.getClass(arg)) && bptype !=
BasicType.L_TYPE)
907 emitImplicitConversion(
BasicType.L_TYPE, ptype, arg);
915 private void emitStoreResult(
Name name)
917 if (name !=
null && name._type() !=
BasicType.V_TYPE)
920 emitStoreInsn(name._type(), name.index());
927 private void emitReturn(
Name onStack)
930 Class rclass = invokerType.returnType();
931 BasicType rtype = lambdaForm.returnType();
936 if (onStack !=
null && onStack._type() !=
BasicType.V_TYPE)
938 ilgen.Emit(OpCodes.Pop);
943 LambdaForm.Name rn = lambdaForm.names[lambdaForm.result];
948 emitLoadInsn(rtype, lambdaForm.result);
951 emitImplicitConversion(rtype, rclass, rn);
953 ilgen.Emit(OpCodes.Ret);
978 if (from.isSubwordOrInt())
986 if (to.isSubwordOrInt())
990 if (to.bitWidth() < 32)
1000 switch (from.name())
1005 case "FLOAT": ilgen.Emit(OpCodes.Conv_R4);
break;
1006 case "DOUBLE": ilgen.Emit(OpCodes.Conv_R8);
break;
1007 default: error =
true;
break;
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;
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;
1032 throw new BailoutException(Bailout.PreconditionViolated,
"unhandled prim cast: " + from +
"2" + to);
1038 private void emitI2X(
Wrapper type)
1040 switch (type.name())
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;
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;
1051 ilgen.EmitLdc_I4(1);
1052 ilgen.Emit(OpCodes.And);
1054 default:
throw new BailoutException(Bailout.PreconditionViolated,
"unknown type: " + type);
1058 private void emitX2I(
Wrapper type)
1060 switch (type.name())
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);
1069 private void EmitConstant(
object obj)
1073 ilgen.Emit(OpCodes.Ldnull);
1076 int index = constants.IndexOf(obj);
1079 index = constants.Count;
1083 ilgen.EmitLdc_I4(index);
1084 ilgen.Emit(OpCodes.Ldelem_Ref);
1087 private void EmitInvokeBasic(global::java.lang.invoke.MethodType mt)
1089 RuntimeJavaType[] args =
new RuntimeJavaType[mt.parameterCount()];
1090 for (
int i = 0; i < args.Length; i++)
1092 args[i] = RuntimeJavaType.FromClass(mt.parameterType(i));
1095 RuntimeJavaType ret = RuntimeJavaType.FromClass(mt.returnType());
1097 Compiler.MethodHandleMethodWrapper.EmitInvokeBasic(JVM.Context, ilgen, args, ret,
false);
1100 private OpCode arrayLoadOpcode(
byte tcode)
1106 return OpCodes.Ldelem_I1;
1108 return OpCodes.Ldelem_U2;
1110 return OpCodes.Ldelem_I2;
1112 return OpCodes.Ldelem_I4;
1114 return OpCodes.Ldelem_I8;
1116 return OpCodes.Ldelem_R4;
1118 return OpCodes.Ldelem_R8;
1120 return OpCodes.Ldelem_Ref;
1122 throw new BailoutException(Bailout.PreconditionViolated,
"tcode = " + tcode);
1126 private bool IsMethodHandleLinkTo(
java.
lang.invoke.MemberName member)
1128 return member.getDeclaringClass() == context.JavaBase.TypeOfJavaLangInvokeMethodHandle.ClassObject && member.getName().StartsWith(
"linkTo", StringComparison.Ordinal);
1131 private bool IsMethodHandleInvokeBasic(
java.
lang.invoke.MemberName member)
1133 return member.getDeclaringClass() == context.JavaBase.TypeOfJavaLangInvokeMethodHandle.ClassObject && member.getName() ==
"invokeBasic";
1136 private RuntimeJavaMethod GetMethodWrapper(
java.
lang.invoke.MemberName member)
1138 return RuntimeJavaType.FromClass(member.getDeclaringClass()).GetMethod(member.getName(), member.getSignature().Replace(
'/',
'.'),
true);
1141 private bool IsStaticallyInvocable(RuntimeJavaMethod mw)
1143 if (mw ==
null || mw.DeclaringType.IsUnloadable || mw.DeclaringType.IsGhost || mw.DeclaringType.IsNonPrimitiveValueType || mw.IsFinalizeOrClone || mw.IsDynamicOnly)
1147 if (mw.ReturnType.IsUnloadable || mw.ReturnType.IsGhost || mw.ReturnType.IsNonPrimitiveValueType)
1151 foreach (RuntimeJavaType tw
in mw.GetParameters())
1153 if (tw.IsUnloadable || tw.IsGhost || tw.IsNonPrimitiveValueType)
1161 private RuntimeJavaField GetFieldWrapper(
java.
lang.invoke.MemberName member)
1163 return RuntimeJavaType.FromClass(member.getDeclaringClass()).GetFieldWrapper(member.getName(), member.getSignature().Replace(
'/',
'.'));
1166 private bool IsStaticallyInvocable(RuntimeJavaField fw)
1169 && !fw.FieldTypeWrapper.IsUnloadable
1170 && !fw.FieldTypeWrapper.IsGhost
1171 && !fw.FieldTypeWrapper.IsNonPrimitiveValueType;