src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java
Index Unified diffs Context diffs Sdiffs Patch New Old Previous File Next File
*** old/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Mon Mar 24 20:12:11 2014
--- new/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Mon Mar 24 20:12:10 2014

*** 23,47 **** --- 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 java.lang.reflect.Modifier; import jdk.internal.org.objectweb.asm.*; ! import java.lang.reflect.*; ! import static java.lang.invoke.LambdaForm.*; + import static java.lang.invoke.LambdaForm.BasicType.*; import static java.lang.invoke.MethodHandleStatics.*; import static java.lang.invoke.MethodHandleNatives.Constants.*; ! import static java.lang.invoke.LambdaForm.BasicType.*; ! 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,89 **** --- 70,93 ---- private final String sourceFile; private final LambdaForm lambdaForm; private final String invokerName; private final MethodType invokerType; private final int[] localsMap; + + /** Info about local variables in compiled lambda form */ + private final int[] localsMap; // index + private final BasicType[] localTypes; // basic type + private final Class<?>[] localClasses; // type /** 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);
*** 95,124 **** --- 99,140 ---- 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 BasicType[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] = V_TYPE; 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; ! index += names[i].type.basicTypeSlots(); ! if (i < names.length) { + BasicType type = names[i].type(); + index += type.basicTypeSlots(); + localTypes[i] = type; + } } } /** instance counters for dumped classes */
*** 145,162 **** --- 161,178 ---- } } 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"); + System.out.println("dump: " + dumpFile); dumpFile.getParentFile().mkdirs(); FileOutputStream file = new FileOutputStream(dumpFile); file.write(classFile); file.close(); return null;
*** 201,211 **** --- 217,227 ---- 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 (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
*** 222,231 **** --- 238,258 ---- 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.
*** 400,409 **** --- 427,460 ---- } private void emitAstoreInsn(int index) { emitStoreInsn(L_TYPE, index); } + private void freeFrameLocal(int oldFrameLocal) { + int i = indexForFrameLocal(oldFrameLocal); + if (i < 0) return; + BasicType 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(BasicType type) { + int frameLocal = localsMap[localsMap.length - 1]; + localsMap[localsMap.length - 1] = frameLocal + type.basicTypeSlots(); + return frameLocal; + } + /** * Emit a boxing call. * * @param wrapper primitive type class to box. */
*** 421,465 **** --- 472,554 ---- */ 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); ! 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(BasicType ptype, Class<?> pclass, Object arg) { assert(basicType(pclass) == ptype); // boxing/unboxing handled by caller if (pclass == ptype.basicTypeClass() && ptype != L_TYPE) return; // nothing to do switch (ptype) { case L_TYPE: if (VerifyType.isNullConversion(Object.class, pclass)) + 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(ptype.basicTypeWrapper(), Wrapper.forPrimitiveType(pclass)); return; if (isStaticallyNameable(pclass)) { ! mv.visitTypeInsn(Opcodes.CHECKCAST, getInternalName(pclass)); + } ! throw newInternalError("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(pclass)); ! mv.visitLdcInsn(constantPlaceholder(cls)); mv.visitTypeInsn(Opcodes.CHECKCAST, CLS); mv.visitInsn(Opcodes.SWAP); mv.visitMethodInsn(Opcodes.INVOKESTATIC, MHI, "castReference", CLL_SIG, false); ! if (pclass.isArray()) ! if (Object[].class.isAssignableFrom(cls)) mv.visitTypeInsn(Opcodes.CHECKCAST, OBJARY); + else if (PROFILE_LEVEL > 0) + mv.visitTypeInsn(Opcodes.CHECKCAST, OBJ); } return; case I_TYPE: if (!VerifyType.isNullConversion(int.class, pclass)) emitPrimCast(ptype.basicTypeWrapper(), Wrapper.forPrimitiveType(pclass)); return; + if (writeBack != null) { + mv.visitInsn(Opcodes.DUP); + emitAstoreInsn(writeBack.index()); } throw new InternalError("bad implicit conversion: tc="+ptype+": "+pclass); } /** * Emits an actual return instruction conforming to the given return type. */
*** 477,486 **** --- 566,579 ---- } 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('.', '/'); } /**
*** 509,534 **** --- 602,633 ---- // 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) {
*** 548,571 **** --- 647,698 ---- 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 + BasicType 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)); ! mv.visitTypeInsn(Opcodes.CHECKCAST, MH); ! emitReferenceCast(MethodHandle.class, target); } else { // load receiver emitAloadInsn(0); ! mv.visitTypeInsn(Opcodes.CHECKCAST, MH); ! emitReferenceCast(MethodHandle.class, null); mv.visitFieldInsn(Opcodes.GETFIELD, MH, "form", LF_SIG); mv.visitFieldInsn(Opcodes.GETFIELD, LF, "names", LFN_SIG); // TODO more to come }
*** 609,618 **** --- 736,747 ---- return true; return false; } 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
*** 632,642 **** --- 761,772 ---- /** * 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()); ! 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
*** 662,671 **** --- 792,849 ---- 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_TYPE) { + 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;
*** 677,686 **** --- 855,887 ---- 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 &&
*** 704,713 **** --- 905,929 ---- 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(...)
*** 759,774 **** --- 975,990 ---- 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); ! 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
*** 776,791 **** --- 992,1010 ---- // 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. *
*** 874,892 **** --- 1093,1111 ---- private void emitPushArgument(Class<?> ptype, Object arg) { BasicType 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); } } } /**
*** 905,918 **** --- 1124,1137 ---- 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(rn.type, lambdaForm.result); ! 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