/** * Emit an invoke for the given name. * * @param name */ void emitInvoke(Name name) { if (true) { // push receiver MethodHandle target = name.function.resolvedHandle; assert(target != null) : name.exprString(); mv.visitLdcInsn(constantPlaceholder(target)); mv.visitTypeInsn(Opcodes.CHECKCAST, MH); } else { // load receiver emitAloadInsn(0); mv.visitTypeInsn(Opcodes.CHECKCAST, MH); mv.visitFieldInsn(Opcodes.GETFIELD, MH, "form", LF_SIG); mv.visitFieldInsn(Opcodes.GETFIELD, LF, "names", LFN_SIG); // TODO more to come } // push arguments for (int i = 0; i < name.arguments.length; i++) { emitPushArgument(name, i); } // invocation MethodType type = name.function.methodType(); mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", type.basicType().toMethodDescriptorString()); }
/** * * @param name * @param paramIndex */ private void emitPushArgument(Name name, int paramIndex) { Object arg = name.arguments[paramIndex]; char ptype = name.function.parameterType(paramIndex); MethodType mtype = name.function.methodType(); if (arg instanceof Name) { Name n = (Name) arg; emitLoadInsn(n.type, n.index()); emitImplicitConversion(n.type, mtype.parameterType(paramIndex)); } else if ((arg == null || arg instanceof String) && ptype == 'L') { emitConst(arg); } else { if (Wrapper.isWrapperType(arg.getClass()) && ptype != 'L') { emitConst(arg); } else { mv.visitLdcInsn(constantPlaceholder(arg)); emitImplicitConversion('L', mtype.parameterType(paramIndex)); } } }
/** * Emit an invoke for the given name. */ void emitInvoke(Name name) { if (true) { // push receiver MethodHandle target = name.function.resolvedHandle; assert(target != null) : name.exprString(); mv.visitLdcInsn(constantPlaceholder(target)); mv.visitTypeInsn(Opcodes.CHECKCAST, MH); } else { // load receiver emitAloadInsn(0); mv.visitTypeInsn(Opcodes.CHECKCAST, MH); mv.visitFieldInsn(Opcodes.GETFIELD, MH, "form", LF_SIG); mv.visitFieldInsn(Opcodes.GETFIELD, LF, "names", LFN_SIG); // TODO more to come } // push arguments for (int i = 0; i < name.arguments.length; i++) { emitPushArgument(name, i); } // invocation MethodType type = name.function.methodType(); mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", type.basicType().toMethodDescriptorString()); }
private void emitPushArgument(Name name, int paramIndex) { Object arg = name.arguments[paramIndex]; char ptype = name.function.parameterType(paramIndex); MethodType mtype = name.function.methodType(); if (arg instanceof Name) { Name n = (Name) arg; emitLoadInsn(n.type, n.index()); emitImplicitConversion(n.type, mtype.parameterType(paramIndex)); } else if ((arg == null || arg instanceof String) && ptype == 'L') { emitConst(arg); } else { if (Wrapper.isWrapperType(arg.getClass()) && ptype != 'L') { emitConst(arg); } else { mv.visitLdcInsn(constantPlaceholder(arg)); emitImplicitConversion('L', mtype.parameterType(paramIndex)); } } }
/** * Wrap a constructor call in a {@link LambdaForm}. * * If constructors ({@code <init>} methods) are called in LFs, problems might arise if the LFs * are turned into bytecode, because the call to the allocator is routed through an MH, and the * verifier cannot find a {@code NEW} instruction preceding the {@code INVOKESPECIAL} to * {@code <init>}. To avoid this, we add an indirection by invoking {@code <init>} through * {@link MethodHandle#linkToSpecial}. * * The last {@link LambdaForm#Name Name} in the argument's form is expected to be the {@code void} * result of the {@code <init>} invocation. This entry is replaced. */ private static MethodHandle linkConstructor(MethodHandle cmh) { final LambdaForm lf = cmh.form; final int initNameIndex = lf.names.length - 1; final Name initName = lf.names[initNameIndex]; final MemberName ctorMN = initName.function.member; final MethodType ctorMT = ctorMN.getInvocationType(); // obtain function member (call target) // linker method type replaces initial parameter (BMH species) with BMH to avoid naming a species (anonymous class!) final MethodType linkerMT = ctorMT.changeParameterType(0, BoundMethodHandle.class).appendParameterTypes(MemberName.class); MemberName linkerMN = new MemberName(MethodHandle.class, "linkToSpecial", linkerMT, REF_invokeStatic); try { linkerMN = MemberName.getFactory().resolveOrFail(REF_invokeStatic, linkerMN, null, NoSuchMethodException.class); assert(linkerMN.isStatic()); } catch (ReflectiveOperationException ex) { throw newInternalError(ex); } // extend arguments array Object[] newArgs = Arrays.copyOf(initName.arguments, initName.arguments.length + 1); newArgs[newArgs.length - 1] = ctorMN; // replace function final NamedFunction nf = new NamedFunction(linkerMN); final Name linkedCtor = new Name(nf, newArgs); linkedCtor.initIndex(initNameIndex); lf.names[initNameIndex] = linkedCtor; return cmh; }
/** * Emit an invoke for the given name, using the MemberName directly. * * @param name */ void emitStaticInvoke(MemberName member, Name name) { assert(member.equals(name.function.member())); String cname = getInternalName(member.getDeclaringClass()); String mname = member.getName(); String mtype; byte refKind = member.getReferenceKind(); if (refKind == REF_invokeSpecial) { // in order to pass the verifier, we need to convert this to invokevirtual in all cases assert(member.canBeStaticallyBound()) : member; refKind = REF_invokeVirtual; } // push arguments for (int i = 0; i < name.arguments.length; i++) { emitPushArgument(name, i); } // invocation if (member.isMethod()) { mtype = member.getMethodType().toMethodDescriptorString(); mv.visitMethodInsn(refKindOpcode(refKind), cname, mname, mtype); } else { mtype = MethodType.toFieldDescriptorString(member.getFieldType()); mv.visitFieldInsn(refKindOpcode(refKind), cname, mname, mtype); } }
/** * Emit bytecode for the selectAlternative idiom. * * The pattern looks like (Cf. MethodHandleImpl.makeGuardWithTest): * * 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} * * @param selectAlternativeName * @param invokeBasicName */ private void emitSelectAlternative(Name selectAlternativeName, Name invokeBasicName) { MethodType type = selectAlternativeName.function.methodType(); Name receiver = (Name) invokeBasicName.arguments[0]; Label L_fallback = new Label(); Label L_done = new Label(); // load test result emitPushArgument(selectAlternativeName, 0); mv.visitInsn(Opcodes.ICONST_1); // if_icmpne L_fallback mv.visitJumpInsn(Opcodes.IF_ICMPNE, L_fallback); // invoke selectAlternativeName.arguments[1] MethodHandle target = (MethodHandle) selectAlternativeName.arguments[1]; emitPushArgument(selectAlternativeName, 1); // get 2nd argument of selectAlternative emitAstoreInsn(receiver.index()); // store the MH in the receiver slot emitInvoke(invokeBasicName); // goto L_done mv.visitJumpInsn(Opcodes.GOTO, L_done); // L_fallback: mv.visitLabel(L_fallback); // invoke selectAlternativeName.arguments[2] MethodHandle fallback = (MethodHandle) selectAlternativeName.arguments[2]; emitPushArgument(selectAlternativeName, 2); // get 3rd argument of selectAlternative emitAstoreInsn(receiver.index()); // store the MH in the receiver slot emitInvoke(invokeBasicName); // L_done: mv.visitLabel(L_done); }
/** * Wrap a constructor call in a {@link LambdaForm}. * * If constructors ({@code <init>} methods) are called in LFs, problems might arise if the LFs * are turned into bytecode, because the call to the allocator is routed through an MH, and the * verifier cannot find a {@code NEW} instruction preceding the {@code INVOKESPECIAL} to * {@code <init>}. To avoid this, we add an indirection by invoking {@code <init>} through * {@link MethodHandle#linkToSpecial}. * * The last {@link LambdaForm.Name Name} in the argument's form is expected to be the {@code void} * result of the {@code <init>} invocation. This entry is replaced. */ private static MethodHandle linkConstructor(MethodHandle cmh) { final LambdaForm lf = cmh.form; final int initNameIndex = lf.names.length - 1; final Name initName = lf.names[initNameIndex]; final MemberName ctorMN = initName.function.member; final MethodType ctorMT = ctorMN.getInvocationType(); // obtain function member (call target) // linker method type replaces initial parameter (BMH species) with BMH to avoid naming a species (anonymous class!) final MethodType linkerMT = ctorMT.changeParameterType(0, BoundMethodHandle.class).appendParameterTypes(MemberName.class); MemberName linkerMN = new MemberName(MethodHandle.class, "linkToSpecial", linkerMT, REF_invokeStatic); try { linkerMN = MemberName.getFactory().resolveOrFail(REF_invokeStatic, linkerMN, null, NoSuchMethodException.class); assert(linkerMN.isStatic()); } catch (ReflectiveOperationException ex) { throw newInternalError(ex); } // extend arguments array Object[] newArgs = Arrays.copyOf(initName.arguments, initName.arguments.length + 1); newArgs[newArgs.length - 1] = ctorMN; // replace function final NamedFunction nf = new NamedFunction(linkerMN); final Name linkedCtor = new Name(nf, newArgs); linkedCtor.initIndex(initNameIndex); lf.names[initNameIndex] = linkedCtor; return cmh; }
/** * Emit an invoke for the given name, using the MemberName directly. */ void emitStaticInvoke(MemberName member, Name name) { assert(member.equals(name.function.member())); String cname = getInternalName(member.getDeclaringClass()); String mname = member.getName(); String mtype; byte refKind = member.getReferenceKind(); if (refKind == REF_invokeSpecial) { // in order to pass the verifier, we need to convert this to invokevirtual in all cases assert(member.canBeStaticallyBound()) : member; refKind = REF_invokeVirtual; } if (member.getDeclaringClass().isInterface() && refKind == REF_invokeVirtual) { // Methods from Object declared in an interface can be resolved by JVM to invokevirtual kind. // Need to convert it back to invokeinterface to pass verification and make the invocation works as expected. refKind = REF_invokeInterface; } // push arguments for (int i = 0; i < name.arguments.length; i++) { emitPushArgument(name, i); } // invocation if (member.isMethod()) { mtype = member.getMethodType().toMethodDescriptorString(); mv.visitMethodInsn(refKindOpcode(refKind), cname, mname, mtype, member.getDeclaringClass().isInterface()); } else { mtype = MethodType.toFieldDescriptorString(member.getFieldType()); mv.visitFieldInsn(refKindOpcode(refKind), cname, mname, mtype); } }
/** * 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 void emitSelectAlternative(Name selectAlternativeName, Name invokeBasicName) { MethodType type = selectAlternativeName.function.methodType(); Name receiver = (Name) invokeBasicName.arguments[0]; Label L_fallback = new Label(); Label L_done = new Label(); // load test result emitPushArgument(selectAlternativeName, 0); mv.visitInsn(Opcodes.ICONST_1); // if_icmpne L_fallback mv.visitJumpInsn(Opcodes.IF_ICMPNE, L_fallback); // invoke selectAlternativeName.arguments[1] MethodHandle target = (MethodHandle) selectAlternativeName.arguments[1]; emitPushArgument(selectAlternativeName, 1); // get 2nd argument of selectAlternative emitAstoreInsn(receiver.index()); // store the MH in the receiver slot emitInvoke(invokeBasicName); // goto L_done mv.visitJumpInsn(Opcodes.GOTO, L_done); // L_fallback: mv.visitLabel(L_fallback); // invoke selectAlternativeName.arguments[2] MethodHandle fallback = (MethodHandle) selectAlternativeName.arguments[2]; emitPushArgument(selectAlternativeName, 2); // get 3rd argument of selectAlternative emitAstoreInsn(receiver.index()); // store the MH in the receiver slot emitInvoke(invokeBasicName); // L_done: mv.visitLabel(L_done); }
/** * Return a {@link LambdaForm.Name} containing a {@link LambdaForm.NamedFunction} that * represents a MH bound to a generic invoker, which in turn forwards to the corresponding * getter. */ Name getterName(Name mhName, int i) { MethodHandle mh = getters[i]; assert(mh != null) : this+"."+i; return new Name(mh, mhName); }
/** * Generate an invoker method for the passed {@link LambdaForm}. */ private byte[] generateCustomizedCodeBytes() { classFilePrologue(); // Suppress this method in backtraces displayed to the user. mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true); // Mark this method as a compiled LambdaForm mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Compiled;", true); // Force inlining of this invoker method. mv.visitAnnotation("Ljava/lang/invoke/ForceInline;", true); // iterate over the form's names, generating bytecode instructions for each // start iterating at the first name following the arguments for (int i = lambdaForm.arity; i < lambdaForm.names.length; i++) { Name name = lambdaForm.names[i]; MemberName member = name.function.member(); if (isSelectAlternative(member)) { // selectAlternative idiom // FIXME: make sure this idiom is really present! emitSelectAlternative(name, lambdaForm.names[i + 1]); i++; // skip MH.invokeBasic of the selectAlternative result } else if (isStaticallyInvocable(member)) { emitStaticInvoke(member, name); } else { emitInvoke(name); } // store the result from evaluating to the target name in a local if required // (if this is the last value, i.e., the one that is going to be returned, // avoid store/load/return and just return) if (i == lambdaForm.names.length - 1 && i == lambdaForm.result) { // return value - do nothing } else if (name.type != 'V') { // non-void: actually assign emitStoreInsn(name.type, name.index()); } } // return statement emitReturn(); classFileEpilogue(); bogusMethod(lambdaForm); final byte[] classFile = cw.toByteArray(); maybeDump(className, classFile); return classFile; }
/** * Emits a return statement from a LF invoker. If required, the result type is cast to the correct return type. */ private void emitReturn() { // return statement if (lambdaForm.result == -1) { // void mv.visitInsn(Opcodes.RETURN); } else { LambdaForm.Name rn = lambdaForm.names[lambdaForm.result]; char rtype = Wrapper.basicTypeChar(invokerType.returnType()); // put return value on the stack if it is not already there if (lambdaForm.result != lambdaForm.names.length - 1) { emitLoadInsn(rn.type, lambdaForm.result); } // potentially generate cast // rtype is the return type of the invoker - generated code must conform to this // rn.type is the type of the result Name in the LF if (rtype != rn.type) { // need cast if (rtype == 'L') { // possibly cast the primitive to the correct type for boxing char boxedType = Wrapper.forWrapperType(invokerType.returnType()).basicTypeChar(); if (boxedType != rn.type) { emitPrimCast(rn.type, boxedType); } // cast primitive to reference ("boxing") emitBoxing(invokerType.returnType()); } else { // to-primitive cast if (rn.type != 'L') { // prim-to-prim cast emitPrimCast(rn.type, rtype); } else { // ref-to-prim cast ("unboxing") throw new InternalError("no ref-to-prim (unboxing) casts supported right now"); } } } // generate actual return statement emitReturnInsn(invokerType.returnType()); } }