src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java
Index Unified diffs Context diffs Sdiffs Patch New Old Previous File Next File jdk Cdiff src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java

src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java

Print this page
rev 9490 : 8037210: Get rid of char-based descriptions 'J' of basic types
Reviewed-by: ?
Contributed-by: john.r.rose@oracle.com
rev 9491 : 8037209: Improvements and cleanups to bytecode assembly for lambda forms
Reviewed-by: ?
Contributed-by: john.r.rose@oracle.com

*** 23,46 **** * questions. */ package java.lang.invoke; - import sun.invoke.util.VerifyAccess; - import static java.lang.invoke.LambdaForm.*; - - import sun.invoke.util.Wrapper; - import java.io.*; import java.util.*; import jdk.internal.org.objectweb.asm.*; - import java.lang.reflect.*; import static java.lang.invoke.MethodHandleStatics.*; import static java.lang.invoke.MethodHandleNatives.Constants.*; import sun.invoke.util.VerifyType; /** * Code generation backend for LambdaForm. * <p> * @author John Rose, JSR 292 EG --- 23,46 ---- * questions. */ package java.lang.invoke; import java.io.*; import java.util.*; + import java.lang.reflect.Modifier; import jdk.internal.org.objectweb.asm.*; import static java.lang.invoke.MethodHandleStatics.*; import static java.lang.invoke.MethodHandleNatives.Constants.*; + import static java.lang.invoke.LambdaForm.*; + import java.lang.invoke.MethodHandleImpl.ArrayAccessor; + import sun.invoke.util.ValueConversions; import sun.invoke.util.VerifyType; + import sun.invoke.util.VerifyAccess; + import sun.invoke.util.Wrapper; /** * Code generation backend for LambdaForm. * <p> * @author John Rose, JSR 292 EG
*** 71,88 **** --- 71,91 ---- private final LambdaForm lambdaForm; private final String invokerName; private final MethodType invokerType; private final int[] localsMap; + private final byte[] localTypes; + private final Class<?>[] localClasses; /** ASM bytecode generation. */ private ClassWriter cw; private MethodVisitor mv; private static final MemberName.Factory MEMBERNAME_FACTORY = MemberName.getFactory(); private static final Class<?> HOST_CLASS = LambdaForm.class; + /** Main constructor; other constructors delegate to this one. */ private InvokerBytecodeGenerator(LambdaForm lambdaForm, int localsMapSize, String className, String invokerName, MethodType invokerType) { if (invokerName.contains(".")) { int p = invokerName.indexOf("."); className = invokerName.substring(0, p);
*** 94,123 **** this.className = superName + "$" + className; this.sourceFile = "LambdaForm$" + className; this.lambdaForm = lambdaForm; this.invokerName = invokerName; this.invokerType = invokerType; ! this.localsMap = new int[localsMapSize]; } private InvokerBytecodeGenerator(String className, String invokerName, MethodType invokerType) { this(null, invokerType.parameterCount(), className, invokerName, invokerType); // Create an array to map name indexes to locals indexes. for (int i = 0; i < localsMap.length; i++) { localsMap[i] = invokerType.parameterSlotCount() - invokerType.parameterSlotDepth(i); } } private InvokerBytecodeGenerator(String className, LambdaForm form, MethodType invokerType) { this(form, form.names.length, className, form.debugName, invokerType); // Create an array to map name indexes to locals indexes. Name[] names = form.names; for (int i = 0, index = 0; i < localsMap.length; i++) { localsMap[i] = index; ! index += basicTypeWrapper(names[i].type).stackSlots(); } } /** instance counters for dumped classes */ --- 97,138 ---- this.className = superName + "$" + className; this.sourceFile = "LambdaForm$" + className; this.lambdaForm = lambdaForm; this.invokerName = invokerName; this.invokerType = invokerType; ! this.localsMap = new int[localsMapSize+1]; ! // last entry of localsMap is count of allocated local slots ! this.localTypes = new byte[localsMapSize+1]; ! this.localClasses = new Class<?>[localsMapSize+1]; } + /** For generating LambdaForm interpreter entry points. */ private InvokerBytecodeGenerator(String className, String invokerName, MethodType invokerType) { this(null, invokerType.parameterCount(), className, invokerName, invokerType); // Create an array to map name indexes to locals indexes. + localTypes[localTypes.length - 1] = T_VOID; for (int i = 0; i < localsMap.length; i++) { localsMap[i] = invokerType.parameterSlotCount() - invokerType.parameterSlotDepth(i); + if (i < invokerType.parameterCount()) + localTypes[i] = basicType(invokerType.parameterType(i)); } } + /** For generating customized code for a single LambdaForm. */ private InvokerBytecodeGenerator(String className, LambdaForm form, MethodType invokerType) { this(form, form.names.length, className, form.debugName, invokerType); // Create an array to map name indexes to locals indexes. Name[] names = form.names; for (int i = 0, index = 0; i < localsMap.length; i++) { localsMap[i] = index; ! if (i < names.length) { ! byte type = names[i].type(); ! index += basicTypeSlots(type); ! localTypes[i] = type; ! } } } /** instance counters for dumped classes */
*** 144,161 **** } } static void maybeDump(final String className, final byte[] classFile) { if (DUMP_CLASS_FILES) { - System.out.println("dump: " + className); java.security.AccessController.doPrivileged( new java.security.PrivilegedAction<Void>() { public Void run() { try { String dumpName = className; //dumpName = dumpName.replace('/', '-'); File dumpFile = new File(DUMP_CLASS_FILES_DIR, dumpName+".class"); dumpFile.getParentFile().mkdirs(); FileOutputStream file = new FileOutputStream(dumpFile); file.write(classFile); file.close(); return null; --- 159,176 ---- } } static void maybeDump(final String className, final byte[] classFile) { if (DUMP_CLASS_FILES) { java.security.AccessController.doPrivileged( new java.security.PrivilegedAction<Void>() { public Void run() { try { String dumpName = className; //dumpName = dumpName.replace('/', '-'); File dumpFile = new File(DUMP_CLASS_FILES_DIR, dumpName+".class"); + System.out.println("dump: " + dumpFile); dumpFile.getParentFile().mkdirs(); FileOutputStream file = new FileOutputStream(dumpFile); file.write(classFile); file.close(); return null;
*** 200,210 **** int cph = 0; // for counting constant placeholders String constantPlaceholder(Object arg) { String cpPlaceholder = "CONSTANT_PLACEHOLDER_" + cph++; ! if (DUMP_CLASS_FILES) cpPlaceholder += " <<" + arg.toString() + ">>"; // debugging aid if (cpPatches.containsKey(cpPlaceholder)) { throw new InternalError("observed CP placeholder twice: " + cpPlaceholder); } // insert placeholder in CP and remember the patch int index = cw.newConst((Object) cpPlaceholder); // TODO check if aready in the constant pool --- 215,225 ---- int cph = 0; // for counting constant placeholders String constantPlaceholder(Object arg) { String cpPlaceholder = "CONSTANT_PLACEHOLDER_" + cph++; ! if (DUMP_CLASS_FILES) cpPlaceholder += " <<" + debugString(arg) + ">>"; // debugging aid if (cpPatches.containsKey(cpPlaceholder)) { throw new InternalError("observed CP placeholder twice: " + cpPlaceholder); } // insert placeholder in CP and remember the patch int index = cw.newConst((Object) cpPlaceholder); // TODO check if aready in the constant pool
*** 221,230 **** --- 236,256 ---- res[p.index] = p.value; } return res; } + private static String debugString(Object arg) { + if (arg instanceof MethodHandle) { + MethodHandle mh = (MethodHandle) arg; + MemberName member = mh.internalMemberName(); + if (member != null) + return member.toString(); + return mh.debugString(); + } + return arg.toString(); + } + /** * Extract the number of constant pool entries from a given class file. * * @param classFile the bytes of the class file in question. * @return the number of entries in the constant pool.
*** 399,408 **** --- 425,458 ---- } private void emitAstoreInsn(int index) { emitStoreInsn(L_TYPE, index); } + private void freeFrameLocal(int oldFrameLocal) { + int i = indexForFrameLocal(oldFrameLocal); + if (i < 0) return; + byte type = localTypes[i]; + int newFrameLocal = makeLocalTemp(type); + mv.visitVarInsn(loadInsnOpcode(type), oldFrameLocal); + mv.visitVarInsn(storeInsnOpcode(type), newFrameLocal); + assert(localsMap[i] == oldFrameLocal); + localsMap[i] = newFrameLocal; + assert(indexForFrameLocal(oldFrameLocal) < 0); + } + private int indexForFrameLocal(int frameLocal) { + for (int i = 0; i < localsMap.length; i++) { + if (localsMap[i] == frameLocal && localTypes[i] != V_TYPE) + return i; + } + return -1; + } + private int makeLocalTemp(byte type) { + int frameLocal = localsMap[localsMap.length - 1]; + localsMap[localsMap.length - 1] = frameLocal + basicTypeSlots(type); + return frameLocal; + } + /** * Emit a boxing call. * * @param wrapper primitive type class to box. */
*** 420,466 **** */ private void emitUnboxing(Wrapper wrapper) { String owner = "java/lang/" + wrapper.wrapperType().getSimpleName(); String name = wrapper.primitiveSimpleName() + "Value"; String desc = "()" + wrapper.basicTypeChar(); ! mv.visitTypeInsn(Opcodes.CHECKCAST, owner); mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, name, desc, false); } /** ! * Emit an implicit conversion. * * @param ptype type of value present on stack * @param pclass type of value required on stack */ ! private void emitImplicitConversion(byte ptype, Class<?> pclass) { assert(basicType(pclass) == ptype); // boxing/unboxing handled by caller if (pclass == basicTypeClass(ptype) && ptype != L_TYPE) return; // nothing to do switch (ptype) { case L_TYPE: ! if (VerifyType.isNullConversion(Object.class, pclass)) return; - if (isStaticallyNameable(pclass)) { - mv.visitTypeInsn(Opcodes.CHECKCAST, getInternalName(pclass)); - } else { - mv.visitLdcInsn(constantPlaceholder(pclass)); - mv.visitTypeInsn(Opcodes.CHECKCAST, CLS); - mv.visitInsn(Opcodes.SWAP); - mv.visitMethodInsn(Opcodes.INVOKESTATIC, MHI, "castReference", CLL_SIG, false); - if (pclass.isArray()) - mv.visitTypeInsn(Opcodes.CHECKCAST, OBJARY); } return; case I_TYPE: ! if (!VerifyType.isNullConversion(int.class, pclass)) emitPrimCast(basicTypeWrapper(ptype), Wrapper.forPrimitiveType(pclass)); return; } throw new InternalError("bad implicit conversion: tc="+ptype+": "+pclass); } /** * Emits an actual return instruction conforming to the given return type. */ private void emitReturnInsn(byte type) { int opcode; --- 470,554 ---- */ private void emitUnboxing(Wrapper wrapper) { String owner = "java/lang/" + wrapper.wrapperType().getSimpleName(); String name = wrapper.primitiveSimpleName() + "Value"; String desc = "()" + wrapper.basicTypeChar(); ! emitReferenceCast(wrapper.wrapperType(), null); mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, name, desc, false); } /** ! * Emit an implicit conversion for an argument which must be of the given pclass. ! * This is usually a no-op, except when pclass is a subword type or a reference other than Object or an interface. * * @param ptype type of value present on stack * @param pclass type of value required on stack + * @param arg compile-time representation of value on stack (Node, constant) or null if none */ ! private void emitImplicitConversion(byte ptype, Class<?> pclass, Object arg) { assert(basicType(pclass) == ptype); // boxing/unboxing handled by caller if (pclass == basicTypeClass(ptype) && ptype != L_TYPE) return; // nothing to do switch (ptype) { case L_TYPE: ! if (VerifyType.isNullConversion(Object.class, pclass, false)) { ! if (PROFILE_LEVEL > 0) ! emitReferenceCast(Object.class, arg); return; } + emitReferenceCast(pclass, arg); return; case I_TYPE: ! if (!VerifyType.isNullConversion(int.class, pclass, false)) emitPrimCast(basicTypeWrapper(ptype), Wrapper.forPrimitiveType(pclass)); return; } throw new InternalError("bad implicit conversion: tc="+ptype+": "+pclass); } + /** Update localClasses type map. Return true if the information is already present. */ + private boolean assertStaticType(Class<?> cls, Name n) { + int local = n.index(); + Class<?> aclass = localClasses[local]; + if (aclass != null && (aclass == cls || cls.isAssignableFrom(aclass))) { + return true; // type info is already present + } else if (aclass == null || aclass.isAssignableFrom(cls)) { + localClasses[local] = cls; // type info can be improved + } + return false; + } + + private void emitReferenceCast(Class<?> cls, Object arg) { + Name writeBack = null; // local to write back result + if (arg instanceof Name) { + Name n = (Name) arg; + if (assertStaticType(cls, n)) + return; // this cast was already performed + if (lambdaForm.useCount(n) > 1) { + // This guy gets used more than once. + writeBack = n; + } + } + if (isStaticallyNameable(cls)) { + String sig = getInternalName(cls); + mv.visitTypeInsn(Opcodes.CHECKCAST, sig); + } else { + mv.visitLdcInsn(constantPlaceholder(cls)); + mv.visitTypeInsn(Opcodes.CHECKCAST, CLS); + mv.visitInsn(Opcodes.SWAP); + mv.visitMethodInsn(Opcodes.INVOKESTATIC, MHI, "castReference", CLL_SIG, false); + if (Object[].class.isAssignableFrom(cls)) + mv.visitTypeInsn(Opcodes.CHECKCAST, OBJARY); + else if (PROFILE_LEVEL > 0) + mv.visitTypeInsn(Opcodes.CHECKCAST, OBJ); + } + if (writeBack != null) { + mv.visitInsn(Opcodes.DUP); + emitAstoreInsn(writeBack.index()); + } + } + /** * Emits an actual return instruction conforming to the given return type. */ private void emitReturnInsn(byte type) { int opcode;
*** 476,485 **** --- 564,577 ---- } mv.visitInsn(opcode); } private static String getInternalName(Class<?> c) { + if (c == Object.class) return OBJ; + else if (c == Object[].class) return OBJARY; + else if (c == Class.class) return CLS; + else if (c == MethodHandle.class) return MH; assert(VerifyAccess.isTypeVisible(c, Object.class)); return c.getName().replace('.', '/'); } /**
*** 508,533 **** --- 600,631 ---- // 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(); + Class<?> rtype = name.function.methodType().returnType(); if (isSelectAlternative(i)) { emitSelectAlternative(name, lambdaForm.names[i + 1]); i++; // skip MH.invokeBasic of the selectAlternative result } else if (isGuardWithCatch(i)) { emitGuardWithCatch(i); i = i+2; // Jump to the end of GWC idiom + } else if (isNewArray(rtype, name)) { + emitNewArray(rtype, name); } else if (isStaticallyInvocable(member)) { + if (isLinkerMethodInvoke(name)) + preserveLinkerMethodTarget(name); emitStaticInvoke(member, name); } else { emitInvoke(name); } // Update cached form name's info in case an intrinsic spanning multiple names was encountered. name = lambdaForm.names[i]; member = name.function.member(); + rtype = name.function.methodType().returnType(); // 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) {
*** 547,570 **** final byte[] classFile = cw.toByteArray(); maybeDump(className, classFile); return classFile; } /** * 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 } --- 645,696 ---- final byte[] classFile = cw.toByteArray(); maybeDump(className, classFile); return classFile; } + /** Certain parts of the JVM expect that, when a call to linkToStatic is made, + * the DMH that produced its MN argument is in local #0. + * This is often true, since the lambda form of a DMH will start with + * the DMH 'l'in arg 0 (local #0) and will end with a call to its linker method. + * But if the DMH is transformed, a new lambda form may be created with + * the DMH placed elsewhere. + */ + private void preserveLinkerMethodTarget(Name name) { + Object lastArg = name.arguments[name.arguments.length - 1]; + Name targetMember = (Name) lastArg; + assert(targetMember.type() == L_TYPE && + !targetMember.isParam() && + isStaticallyInvocable(targetMember.function.member()) && + targetMember.arguments.length == 1); + Object targetDMH = DirectMethodHandle.findDirectMethodHandle(targetMember); + assert(targetDMH != null); + if (!(targetDMH instanceof Name && ((Name)targetDMH).index() == 0)) { + // Oops, the DMH is not in a local #0, or is a constant. + int DMH_FRAME_INDEX = 0; // hardwired to local #0 + byte DMH_TYPE = L_TYPE; + freeFrameLocal(DMH_FRAME_INDEX); + // Save it to local #0: + emitPushArgument(Object.class, targetDMH); + mv.visitVarInsn(storeInsnOpcode(DMH_TYPE), DMH_FRAME_INDEX); + } + } + /** * 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)); ! emitReferenceCast(MethodHandle.class, 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 }
*** 606,617 **** --- 732,753 ---- return true; // in java.lang.invoke package if (member.isPublic() && isStaticallyNameable(cls)) return true; return false; } + static { + // There's a dependency between ArrayAccessor and InvokerBytecodeGenerator, so + // ArrayAccessor might not be fully initialized yet. + assert(ArrayAccessor.OBJECT_ARRAY_GETTER == null || + isStaticallyInvocable(ArrayAccessor.OBJECT_ARRAY_GETTER.internalMemberName())); + assert(ArrayAccessor.OBJECT_ARRAY_SETTER == null || + isStaticallyInvocable(ArrayAccessor.OBJECT_ARRAY_SETTER.internalMemberName())); + } 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 // could use VerifyAccess.isClassAccessible but the following is a safe approximation
*** 631,641 **** /** * 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 --- 767,778 ---- /** * Emit an invoke for the given name, using the MemberName directly. */ void emitStaticInvoke(MemberName member, Name name) { assert(member.equals(name.function.member())); ! Class<?> defc = member.getDeclaringClass(); ! String cname = getInternalName(defc); 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
*** 653,670 **** 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); } } int refKindOpcode(byte refKind) { switch (refKind) { case REF_invokeVirtual: return Opcodes.INVOKEVIRTUAL; case REF_invokeStatic: return Opcodes.INVOKESTATIC; --- 790,861 ---- for (int i = 0; i < name.arguments.length; i++) { emitPushArgument(name, i); } // invocation ! if (defc == ArrayAccessor.class && ! match(member, ArrayAccessor.OBJECT_ARRAY_GETTER)) { ! mv.visitInsn(Opcodes.AALOAD); ! } else if (defc == ArrayAccessor.class && ! match(member, ArrayAccessor.OBJECT_ARRAY_SETTER)) { ! mv.visitInsn(Opcodes.AASTORE); ! } else 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); } + // Issue a type assertion for the result, so we can avoid casts later. + if (name.type == 'L') { + Class<?> rtype = member.getInvocationType().returnType(); + assert(!rtype.isPrimitive()); + if (rtype != Object.class && !rtype.isInterface()) { + assertStaticType(rtype, name); + } + } + } + + boolean isNewArray(Class<?> rtype, Name name) { + return rtype.isArray() && + isStaticallyNameable(rtype) && + isArrayBuilder(name.function.resolvedHandle) && + name.arguments.length > 0; + } + + void emitNewArray(Class<?> rtype, Name name) throws InternalError { + Class<?> arrayElementType = rtype.getComponentType(); + emitIconstInsn(name.arguments.length); + int xas; + if (!arrayElementType.isPrimitive()) { + mv.visitTypeInsn(Opcodes.ANEWARRAY, getInternalName(arrayElementType)); + xas = Opcodes.AASTORE; + } else { + int tc; + switch (Wrapper.forPrimitiveType(arrayElementType)) { + case BOOLEAN: tc = Opcodes.T_BOOLEAN; xas = Opcodes.BASTORE; break; + case BYTE: tc = Opcodes.T_BYTE; xas = Opcodes.BASTORE; break; + case CHAR: tc = Opcodes.T_CHAR; xas = Opcodes.CASTORE; break; + case SHORT: tc = Opcodes.T_SHORT; xas = Opcodes.SASTORE; break; + case INT: tc = Opcodes.T_INT; xas = Opcodes.IASTORE; break; + case LONG: tc = Opcodes.T_LONG; xas = Opcodes.LASTORE; break; + case FLOAT: tc = Opcodes.T_FLOAT; xas = Opcodes.FASTORE; break; + case DOUBLE: tc = Opcodes.T_DOUBLE; xas = Opcodes.DASTORE; break; + default: throw new InternalError(rtype.getName()); + } + mv.visitIntInsn(Opcodes.NEWARRAY, tc); + } + // store arguments + for (int i = 0; i < name.arguments.length; i++) { + mv.visitInsn(Opcodes.DUP); + emitIconstInsn(i); + emitPushArgument(name, i); + mv.visitInsn(xas); + } + // the array is left on the stack + assertStaticType(rtype, name); } int refKindOpcode(byte refKind) { switch (refKind) { case REF_invokeVirtual: return Opcodes.INVOKEVIRTUAL; case REF_invokeStatic: return Opcodes.INVOKESTATIC;
*** 676,685 **** --- 867,899 ---- case REF_putStatic: return Opcodes.PUTSTATIC; } throw new InternalError("refKind="+refKind); } + static boolean isArrayBuilder(MethodHandle fn) { + if (fn == null) + return false; + MethodType mtype = fn.type(); + Class<?> rtype = mtype.returnType(); + Class<?> arrayElementType = rtype.getComponentType(); + if (arrayElementType == null) + return false; + List<Class<?>> ptypes = mtype.parameterList(); + int size = ptypes.size(); + if (!ptypes.equals(Collections.nCopies(size, arrayElementType))) + return false; + // Assume varargsArray caches pointers. + if (fn != ValueConversions.varargsArray(rtype, size)) + return false; + return true; + } + + static boolean match(MemberName member, MethodHandle fn) { + if (member == null || fn == null) return false; + return member.equals(fn.internalMemberName()); + } + /** * Check if MemberName is a call to a method named {@code name} in class {@code declaredClass}. */ private boolean memberRefersTo(MemberName member, Class<?> declaringClass, String name) { return member != null &&
*** 703,712 **** --- 917,941 ---- return memberRefersTo(member, MethodHandle.class, "invokeBasic") && !member.isPublic() && !member.isStatic(); } /** + * Check if MemberName is a call to MethodHandle.linkToStatic, etc. + */ + private boolean isLinkerMethodInvoke(Name name) { + if (name.function == null) + return false; + if (name.arguments.length < 1) + return false; // must have MH argument + MemberName member = name.function.member(); + return member != null && + member.getDeclaringClass() == MethodHandle.class && + !member.isPublic() && member.isStatic() && + member.getName().startsWith("linkTo"); + } + + /** * Check if i-th name is a call to MethodHandleImpl.selectAlternative. */ private boolean isSelectAlternative(int pos) { // selectAlternative idiom: // t_{n}:L=MethodHandleImpl.selectAlternative(...)
*** 758,773 **** 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] emitPushArgument(selectAlternativeName, 1); // get 2nd argument of selectAlternative emitAstoreInsn(receiver.index()); // store the MH in the receiver slot emitInvoke(invokeBasicName); // goto L_done --- 987,1002 ---- Label L_fallback = new Label(); Label L_done = new Label(); // load test result emitPushArgument(selectAlternativeName, 0); // if_icmpne L_fallback ! mv.visitJumpInsn(Opcodes.IFEQ, 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 emitInvoke(invokeBasicName); // goto L_done
*** 775,790 **** --- 1004,1022 ---- // L_fallback: mv.visitLabel(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 emitInvoke(invokeBasicName); // L_done: mv.visitLabel(L_done); + // for now do not bother to merge typestate; just reset to the dominator state + System.arraycopy(preForkClasses, 0, localClasses, 0, preForkClasses.length); } /** * Emit bytecode for the guardWithCatch idiom. *
*** 873,891 **** private void emitPushArgument(Class<?> ptype, Object arg) { byte bptype = basicType(ptype); if (arg instanceof Name) { Name n = (Name) arg; emitLoadInsn(n.type, n.index()); ! emitImplicitConversion(n.type, ptype); } else if ((arg == null || arg instanceof String) && bptype == L_TYPE) { emitConst(arg); } else { if (Wrapper.isWrapperType(arg.getClass()) && bptype != L_TYPE) { emitConst(arg); } else { mv.visitLdcInsn(constantPlaceholder(arg)); ! emitImplicitConversion(L_TYPE, ptype); } } } /** --- 1105,1123 ---- private void emitPushArgument(Class<?> ptype, Object arg) { byte bptype = basicType(ptype); if (arg instanceof Name) { Name n = (Name) arg; emitLoadInsn(n.type, n.index()); ! emitImplicitConversion(n.type, ptype, n); } else if ((arg == null || arg instanceof String) && bptype == L_TYPE) { emitConst(arg); } else { if (Wrapper.isWrapperType(arg.getClass()) && bptype != L_TYPE) { emitConst(arg); } else { mv.visitLdcInsn(constantPlaceholder(arg)); ! emitImplicitConversion(L_TYPE, ptype, arg); } } } /**
*** 902,916 **** // it doesn't matter what rclass is; the JVM will discard any value } else { LambdaForm.Name rn = lambdaForm.names[lambdaForm.result]; // put return value on the stack if it is not already there ! if (lambdaForm.result != lambdaForm.names.length - 1) { ! emitLoadInsn(rn.type, lambdaForm.result); } ! emitImplicitConversion(rtype, rclass); // generate actual return statement emitReturnInsn(rtype); } } --- 1134,1149 ---- // it doesn't matter what rclass is; the JVM will discard any value } else { LambdaForm.Name rn = lambdaForm.names[lambdaForm.result]; // put return value on the stack if it is not already there ! if (lambdaForm.result != lambdaForm.names.length - 1 || ! lambdaForm.result < lambdaForm.arity) { ! emitLoadInsn(rtype, lambdaForm.result); } ! emitImplicitConversion(rtype, rclass, rn); // generate actual return statement emitReturnInsn(rtype); } }
src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java
Index Unified diffs Context diffs Sdiffs Patch New Old Previous File Next File