private void emitPushArguments(Name args, int start) { for (int i = start; i < args.arguments.Length; i++) { emitPushArgument(args, i); } }
private void emitPushArgument(Name name, int paramIndex) { object arg = name.arguments[paramIndex]; Class ptype = name.function.methodType().parameterType(paramIndex); emitPushArgument(ptype, arg); }
private void emitPushArgument(Class ptype, object arg) { BasicType bptype = BasicType.basicType(ptype); if (arg is Name) { Name n = (Name)arg; emitLoadInsn(n._type(), n.index()); emitImplicitConversion(n._type(), ptype, n); } else if ((arg == null || arg is string) && bptype == BasicType.L_TYPE) { emitConst(arg); } else { if (Wrapper.isWrapperType(ikvm.extensions.ExtensionMethods.getClass(arg)) && bptype != BasicType.L_TYPE) { emitConst(arg); } else { EmitConstant(arg); emitImplicitConversion(BasicType.L_TYPE, ptype, arg); } } }
/** * Emit an invoke for the given name. */ void emitInvoke(Name name) { //assert(!isLinkerMethodInvoke(name)); // should use the static path for these if (true) { // push receiver MethodHandle target = name.function._resolvedHandle(); //assert(target != null) : name.exprString(); //mv.visitLdcInsn(constantPlaceholder(target)); EmitConstant(target); emitReferenceCast(CoreClasses.java.lang.invoke.MethodHandle.Wrapper.ClassObject, target); } else { // load receiver //emitAloadInsn(0); //emitReferenceCast(MethodHandle.class, null); //mv.visitFieldInsn(Opcodes.GETFIELD, MH, "form", LF_SIG); //mv.visitFieldInsn(Opcodes.GETFIELD, LF, "names", LFN_SIG); // TODO more to come } // push arguments emitPushArguments(name); // invocation MethodType type = name.function.methodType(); //mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", type.basicType().toMethodDescriptorString(), false); EmitInvokeBasic(type.basicType()); }
/** * Emits a return statement from a LF invoker. If required, the result type is cast to the correct return type. */ private void emitReturn(Name onStack) { // return statement Class rclass = invokerType.returnType(); BasicType rtype = lambdaForm.returnType(); //assert(rtype == basicType(rclass)); // must agree if (rtype == BasicType.V_TYPE) { // [IKVM] unlike the JVM, the CLR doesn't like left over values on the stack if (onStack != null && onStack._type() != BasicType.V_TYPE) { ilgen.Emit(OpCodes.Pop); } } else { LambdaForm.Name rn = lambdaForm.names[lambdaForm.result]; // put return value on the stack if it is not already there if (rn != onStack) { emitLoadInsn(rtype, lambdaForm.result); } emitImplicitConversion(rtype, rclass, rn); } ilgen.Emit(OpCodes.Ret); }
/** * Store the name to its local, if necessary. */ private void emitStoreResult(Name name) { if (name != null && name._type() != BasicType.V_TYPE) { // non-void: actually assign emitStoreInsn(name._type(), name.index()); } }
void emitNewArray(Name name) { Class rtype = name.function.methodType().returnType(); if (name.arguments.Length == 0) { // The array will be a constant. object emptyArray; try { emptyArray = name.function._resolvedHandle().invoke(); } catch (Exception ex) { throw new java.lang.InternalError(ex); } //assert(java.lang.reflect.Array.getLength(emptyArray) == 0); //assert(emptyArray.getClass() == rtype); // exact typing //mv.visitLdcInsn(constantPlaceholder(emptyArray)); EmitConstant(emptyArray); emitReferenceCast(rtype, emptyArray); return; } Class arrayElementType = rtype.getComponentType(); //assert(arrayElementType != null); emitIconstInsn(name.arguments.Length); OpCode xas = OpCodes.Stelem_Ref; if (!arrayElementType.isPrimitive()) { TypeWrapper tw = TypeWrapper.FromClass(arrayElementType); if (tw.IsUnloadable || tw.IsGhost || tw.IsGhostArray || tw.IsNonPrimitiveValueType) { throw new BailoutException(Bailout.UnsupportedArrayType, tw); } ilgen.Emit(OpCodes.Newarr, tw.TypeAsArrayType); } else { byte tc = arrayTypeCode(Wrapper.forPrimitiveType(arrayElementType)); xas = arrayInsnOpcode(tc); //mv.visitIntInsn(Opcodes.NEWARRAY, tc); ilgen.Emit(OpCodes.Newarr, TypeWrapper.FromClass(arrayElementType).TypeAsArrayType); } // store arguments for (int i = 0; i < name.arguments.Length; i++) { //mv.visitInsn(Opcodes.DUP); ilgen.Emit(OpCodes.Dup); emitIconstInsn(i); emitPushArgument(name, i); //mv.visitInsn(xas); ilgen.Emit(xas); } // the array is left on the stack assertStaticType(rtype, name); }
void emitArrayLoad(Name name) { OpCode arrayOpcode = OpCodes.Ldelem_Ref; Class elementType = name.function.methodType().parameterType(0).getComponentType(); emitPushArguments(name); if (elementType.isPrimitive()) { Wrapper w = Wrapper.forPrimitiveType(elementType); arrayOpcode = arrayLoadOpcode(arrayTypeCode(w)); } ilgen.Emit(arrayOpcode); }
/** * Emit bytecode for the selectAlternative idiom. * * The pattern looks like (Cf. MethodHandleImpl.makeGuardWithTest): * <blockquote><pre>{@code * Lambda(a0:L,a1:I)=>{ * t2:I=foo.test(a1:I); * t3:L=MethodHandleImpl.selectAlternative(t2:I,(MethodHandle(int)int),(MethodHandle(int)int)); * t4:I=MethodHandle.invokeBasic(t3:L,a1:I);t4:I} * }</pre></blockquote> */ private Name emitSelectAlternative(Name selectAlternativeName, Name invokeBasicName) { //assert isStaticallyInvocable(invokeBasicName); Name receiver = (Name)invokeBasicName.arguments[0]; CodeEmitterLabel L_fallback = ilgen.DefineLabel(); CodeEmitterLabel L_done = ilgen.DefineLabel(); // load test result emitPushArgument(selectAlternativeName, 0); // if_icmpne L_fallback ilgen.EmitBrfalse(L_fallback); // invoke selectAlternativeName.arguments[1] //Class<?>[] preForkClasses = localClasses.clone(); emitPushArgument(selectAlternativeName, 1); // get 2nd argument of selectAlternative emitAstoreInsn(receiver.index()); // store the MH in the receiver slot emitStaticInvoke(invokeBasicName); // goto L_done ilgen.EmitBr(L_done); // L_fallback: ilgen.MarkLabel(L_fallback); // invoke selectAlternativeName.arguments[2] //System.arraycopy(preForkClasses, 0, localClasses, 0, preForkClasses.length); emitPushArgument(selectAlternativeName, 2); // get 3rd argument of selectAlternative emitAstoreInsn(receiver.index()); // store the MH in the receiver slot emitStaticInvoke(invokeBasicName); // L_done: ilgen.MarkLabel(L_done); // for now do not bother to merge typestate; just reset to the dominator state //System.arraycopy(preForkClasses, 0, localClasses, 0, preForkClasses.length); return(invokeBasicName); // return what's on stack }
private void emitPushArguments(Name args) { emitPushArguments(args, 0); }
private NativeInvokerBytecodeGenerator(LambdaForm lambdaForm, MethodType invokerType) { if (invokerType != invokerType.basicType()) { throw new BailoutException(Bailout.NotBasicType, invokerType); } this.lambdaForm = lambdaForm; this.invokerType = invokerType; this.delegateType = MethodHandleUtil.GetMemberWrapperDelegateType(invokerType); MethodInfo mi = MethodHandleUtil.GetDelegateInvokeMethod(delegateType); Type[] paramTypes = MethodHandleUtil.GetParameterTypes(typeof(object[]), mi); // 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) this.dm = new DynamicMethod(lambdaForm.debugName, mi.ReturnType, paramTypes, typeof(object).Module, true); this.ilgen = CodeEmitter.Create(this.dm); if (invokerType.parameterCount() > MethodHandleUtil.MaxArity) { this.packedArgType = paramTypes[paramTypes.Length - 1]; this.packedArgPos = paramTypes.Length - 1; } else { this.packedArgPos = Int32.MaxValue; } locals = new CodeEmitterLocal[lambdaForm.names.Length]; for (int i = lambdaForm._arity(); i < lambdaForm.names.Length; i++) { Name name = lambdaForm.names[i]; if (name.index() != i) { throw new BailoutException(Bailout.PreconditionViolated, "name.index() != i"); } switch (name.typeChar()) { case 'L': locals[i] = ilgen.DeclareLocal(Types.Object); break; case 'I': locals[i] = ilgen.DeclareLocal(Types.Int32); break; case 'J': locals[i] = ilgen.DeclareLocal(Types.Int64); break; case 'F': locals[i] = ilgen.DeclareLocal(Types.Single); break; case 'D': locals[i] = ilgen.DeclareLocal(Types.Double); break; case 'V': break; default: throw new BailoutException(Bailout.PreconditionViolated, "Unsupported typeChar(): " + name.typeChar()); } } }
/** * Emit bytecode for the guardWithCatch idiom. * * The pattern looks like (Cf. MethodHandleImpl.makeGuardWithCatch): * <blockquote><pre>{@code * guardWithCatch=Lambda(a0:L,a1:L,a2:L,a3:L,a4:L,a5:L,a6:L,a7:L)=>{ * t8:L=MethodHandle.invokeBasic(a4:L,a6:L,a7:L); * t9:L=MethodHandleImpl.guardWithCatch(a1:L,a2:L,a3:L,t8:L); * t10:I=MethodHandle.invokeBasic(a5:L,t9:L);t10:I} * }</pre></blockquote> * * It is compiled into bytecode equivalent of the following code: * <blockquote><pre>{@code * try { * return a1.invokeBasic(a6, a7); * } catch (Throwable e) { * if (!a2.isInstance(e)) throw e; * return a3.invokeBasic(ex, a6, a7); * }}</pre></blockquote> */ private Name emitGuardWithCatch(int pos) { Name args = lambdaForm.names[pos]; Name invoker = lambdaForm.names[pos + 1]; Name result = lambdaForm.names[pos + 2]; CodeEmitterLabel L_handler = ilgen.DefineLabel(); CodeEmitterLabel L_done = ilgen.DefineLabel(); Class returnType = result.function._resolvedHandle().type().returnType(); MethodType type = args.function._resolvedHandle().type() .dropParameterTypes(0, 1) .changeReturnType(returnType); // Normal case ilgen.BeginExceptionBlock(); // load target emitPushArgument(invoker, 0); emitPushArguments(args, 1); // skip 1st argument: method handle EmitInvokeBasic(type.basicType()); CodeEmitterLocal returnValue = null; if (returnType != java.lang.Void.TYPE) { returnValue = ilgen.DeclareLocal(TypeWrapper.FromClass(returnType).TypeAsLocalOrStackType); ilgen.Emit(OpCodes.Stloc, returnValue); } ilgen.EmitLeave(L_done); // Exceptional case ilgen.BeginCatchBlock(typeof(Exception)); // [IKVM] map the exception and store it in a local and exit the handler ilgen.EmitLdc_I4(0); ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.mapException.MakeGenericMethod(typeof(Exception))); CodeEmitterLocal exception = ilgen.DeclareLocal(typeof(Exception)); ilgen.Emit(OpCodes.Stloc, exception); ilgen.EmitLeave(L_handler); ilgen.EndExceptionBlock(); // Check exception's type ilgen.MarkLabel(L_handler); // load exception class emitPushArgument(invoker, 1); ilgen.Emit(OpCodes.Ldloc, exception); CoreClasses.java.lang.Class.Wrapper.GetMethodWrapper("isInstance", "(Ljava.lang.Object;)Z", false).EmitCall(ilgen); CodeEmitterLabel L_rethrow = ilgen.DefineLabel(); ilgen.EmitBrfalse(L_rethrow); // Invoke catcher // load catcher emitPushArgument(invoker, 2); ilgen.Emit(OpCodes.Ldloc, exception); emitPushArguments(args, 1); // skip 1st argument: method handle MethodType catcherType = type.insertParameterTypes(0, CoreClasses.java.lang.Throwable.Wrapper.ClassObject); EmitInvokeBasic(catcherType.basicType()); if (returnValue != null) { ilgen.Emit(OpCodes.Stloc, returnValue); } ilgen.EmitBr(L_done); ilgen.MarkLabel(L_rethrow); ilgen.Emit(OpCodes.Ldloc, exception); ilgen.Emit(OpCodes.Call, Compiler.unmapExceptionMethod); ilgen.Emit(OpCodes.Throw); ilgen.MarkLabel(L_done); if (returnValue != null) { ilgen.Emit(OpCodes.Ldloc, returnValue); } return(result); }
/* * static boolean isStaticallyNameable(Class<?> cls) { * if (cls == Object.class) * return true; * while (cls.isArray()) * cls = cls.getComponentType(); * if (cls.isPrimitive()) * return true; // int[].class, for example * if (ReflectUtil.isVMAnonymousClass(cls)) // FIXME: switch to supported API once it is added * return false; * // could use VerifyAccess.isClassAccessible but the following is a safe approximation * if (cls.getClassLoader() != Object.class.getClassLoader()) * return false; * if (VerifyAccess.isSamePackage(MethodHandle.class, cls)) * return true; * if (!Modifier.isPublic(cls.getModifiers())) * return false; * for (Class<?> pkgcls : STATICALLY_INVOCABLE_PACKAGES) { * if (VerifyAccess.isSamePackage(pkgcls, cls)) * return true; * } * return false; * } */ void emitStaticInvoke(Name name) { emitStaticInvoke(name.function._member(), name); }
/** * Generate an invoker method for the passed {@link LambdaForm}. */ private Delegate generateCustomizedCodeBytes() { // iterate over the form's names, generating bytecode instructions for each // start iterating at the first name following the arguments Name onStack = null; for (int i = lambdaForm._arity(); i < lambdaForm.names.Length; i++) { Name name = lambdaForm.names[i]; emitStoreResult(onStack); onStack = name; // unless otherwise modified below MethodHandleImpl.Intrinsic intr = name.function.intrinsicName(); switch (intr.name()) { case "SELECT_ALTERNATIVE": //assert isSelectAlternative(i); onStack = emitSelectAlternative(name, lambdaForm.names[i + 1]); i++; // skip MH.invokeBasic of the selectAlternative result continue; case "GUARD_WITH_CATCH": //assert isGuardWithCatch(i); onStack = emitGuardWithCatch(i); i = i + 2; // Jump to the end of GWC idiom continue; case "NEW_ARRAY": Class rtype = name.function.methodType().returnType(); if (InvokerBytecodeGenerator.isStaticallyNameable(rtype)) { emitNewArray(name); continue; } break; case "ARRAY_LOAD": emitArrayLoad(name); continue; case "IDENTITY": //assert(name.arguments.length == 1); emitPushArguments(name); continue; case "NONE": // no intrinsic associated break; // [IKVM] ARRAY_STORE and ZERO appear to be unused default: throw new BailoutException(Bailout.UnsupportedIntrinsic, "Unknown intrinsic: " + intr); } MemberName member = name.function._member(); if (isStaticallyInvocable(member)) { emitStaticInvoke(member, name); } else { emitInvoke(name); } } // return statement emitReturn(onStack); ilgen.DoEmit(); return(dm.CreateDelegate(delegateType, constants.ToArray())); }
/** Update localClasses type map. Return true if the information is already present. */ private void assertStaticType(Class cls, Name n) { // [IKVM] not implemented }
/** * Emit an invoke for the given name, using the MemberName directly. */ void emitStaticInvoke(MemberName member, Name name) { // push arguments emitPushArguments(name); // invocation if (member.isMethod()) { if (IsMethodHandleLinkTo(member)) { MethodType mt = member.getMethodType(); TypeWrapper[] args = new TypeWrapper[mt.parameterCount()]; for (int j = 0; j < args.Length; j++) { args[j] = TypeWrapper.FromClass(mt.parameterType(j)); args[j].Finish(); } TypeWrapper ret = TypeWrapper.FromClass(mt.returnType()); ret.Finish(); Compiler.MethodHandleMethodWrapper.EmitLinkToCall(ilgen, args, ret); ret.EmitConvSignatureTypeToStackType(ilgen); } else if (IsMethodHandleInvokeBasic(member)) { EmitInvokeBasic(member.getMethodType()); } else { switch (member.getReferenceKind()) { case MethodHandleNatives.Constants.REF_invokeInterface: case MethodHandleNatives.Constants.REF_invokeSpecial: case MethodHandleNatives.Constants.REF_invokeStatic: case MethodHandleNatives.Constants.REF_invokeVirtual: break; default: throw new BailoutException(Bailout.UnsupportedRefKind, member); } MethodWrapper mw = GetMethodWrapper(member); if (!IsStaticallyInvocable(mw)) { throw new BailoutException(Bailout.NotStaticallyInvocable, member); } mw.Link(); mw.DeclaringType.Finish(); mw.ResolveMethod(); if (mw.HasCallerID) { EmitConstant(DynamicCallerIDProvider.Instance); ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicCallerID); } if (mw.IsStatic || member.getReferenceKind() == MethodHandleNatives.Constants.REF_invokeSpecial) { mw.EmitCall(ilgen); } else { mw.EmitCallvirt(ilgen); } mw.ReturnType.EmitConvSignatureTypeToStackType(ilgen); } } else if (member.isField()) { FieldWrapper fw = GetFieldWrapper(member); if (!IsStaticallyInvocable(fw)) { throw new BailoutException(Bailout.NotStaticallyInvocable, member); } fw.Link(); fw.DeclaringType.Finish(); fw.ResolveField(); switch (member.getReferenceKind()) { case MethodHandleNatives.Constants.REF_getField: case MethodHandleNatives.Constants.REF_getStatic: fw.EmitGet(ilgen); fw.FieldTypeWrapper.EmitConvSignatureTypeToStackType(ilgen); break; case MethodHandleNatives.Constants.REF_putField: case MethodHandleNatives.Constants.REF_putStatic: fw.EmitSet(ilgen); break; default: throw new BailoutException(Bailout.UnsupportedRefKind, member); } } else { throw new BailoutException(Bailout.NotStaticallyInvocable, member); } }
/* static boolean isStaticallyNameable(Class<?> cls) { if (cls == Object.class) return true; while (cls.isArray()) cls = cls.getComponentType(); if (cls.isPrimitive()) return true; // int[].class, for example if (ReflectUtil.isVMAnonymousClass(cls)) // FIXME: switch to supported API once it is added return false; // could use VerifyAccess.isClassAccessible but the following is a safe approximation if (cls.getClassLoader() != Object.class.getClassLoader()) return false; if (VerifyAccess.isSamePackage(MethodHandle.class, cls)) return true; if (!Modifier.isPublic(cls.getModifiers())) return false; for (Class<?> pkgcls : STATICALLY_INVOCABLE_PACKAGES) { if (VerifyAccess.isSamePackage(pkgcls, cls)) return true; } return false; } */ void emitStaticInvoke(Name name) { emitStaticInvoke(name.function._member(), name); }
/** * Emit bytecode for the selectAlternative idiom. * * The pattern looks like (Cf. MethodHandleImpl.makeGuardWithTest): * <blockquote><pre>{@code * Lambda(a0:L,a1:I)=>{ * t2:I=foo.test(a1:I); * t3:L=MethodHandleImpl.selectAlternative(t2:I,(MethodHandle(int)int),(MethodHandle(int)int)); * t4:I=MethodHandle.invokeBasic(t3:L,a1:I);t4:I} * }</pre></blockquote> */ private Name emitSelectAlternative(Name selectAlternativeName, Name invokeBasicName) { //assert isStaticallyInvocable(invokeBasicName); Name receiver = (Name) invokeBasicName.arguments[0]; CodeEmitterLabel L_fallback = ilgen.DefineLabel(); CodeEmitterLabel L_done = ilgen.DefineLabel(); // load test result emitPushArgument(selectAlternativeName, 0); // if_icmpne L_fallback ilgen.EmitBrfalse(L_fallback); // invoke selectAlternativeName.arguments[1] //Class<?>[] preForkClasses = localClasses.clone(); emitPushArgument(selectAlternativeName, 1); // get 2nd argument of selectAlternative emitAstoreInsn(receiver.index()); // store the MH in the receiver slot emitStaticInvoke(invokeBasicName); // goto L_done ilgen.EmitBr(L_done); // L_fallback: ilgen.MarkLabel(L_fallback); // invoke selectAlternativeName.arguments[2] //System.arraycopy(preForkClasses, 0, localClasses, 0, preForkClasses.length); emitPushArgument(selectAlternativeName, 2); // get 3rd argument of selectAlternative emitAstoreInsn(receiver.index()); // store the MH in the receiver slot emitStaticInvoke(invokeBasicName); // L_done: ilgen.MarkLabel(L_done); // for now do not bother to merge typestate; just reset to the dominator state //System.arraycopy(preForkClasses, 0, localClasses, 0, preForkClasses.length); return invokeBasicName; // return what's on stack }