# HG changeset patch # User vlivanov # Date 1410362387 -14400 # Node ID d69abed3a07d23895a9620eb5981eecfc3ace8a7 # Parent 3bbb6a284bd43d440ddeef8a633cbf11d850fadd 8050052: Small cleanups in java.lang.invoke code Reviewed-by: vlivanov, psandoz Contributed-by: john.r.rose@oracle.com diff -r 3bbb6a284bd4 -r d69abed3a07d jdk/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java --- a/jdk/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java Wed Sep 10 19:19:47 2014 +0400 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java Wed Sep 10 19:19:47 2014 +0400 @@ -50,9 +50,9 @@ * * All bound arguments are encapsulated in dedicated species. */ -/* non-public */ abstract class BoundMethodHandle extends MethodHandle { +/*non-public*/ abstract class BoundMethodHandle extends MethodHandle { - /* non-public */ BoundMethodHandle(MethodType type, LambdaForm form) { + /*non-public*/ BoundMethodHandle(MethodType type, LambdaForm form) { super(type, form); } @@ -66,15 +66,15 @@ switch (xtype) { case L_TYPE: if (true) return bindSingle(type, form, x); // Use known fast path. - return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(L_TYPE).constructor[0].invokeBasic(type, form, x); + return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(L_TYPE).constructor().invokeBasic(type, form, x); case I_TYPE: - return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(I_TYPE).constructor[0].invokeBasic(type, form, ValueConversions.widenSubword(x)); + return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(I_TYPE).constructor().invokeBasic(type, form, ValueConversions.widenSubword(x)); case J_TYPE: - return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(J_TYPE).constructor[0].invokeBasic(type, form, (long) x); + return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(J_TYPE).constructor().invokeBasic(type, form, (long) x); case F_TYPE: - return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(F_TYPE).constructor[0].invokeBasic(type, form, (float) x); + return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(F_TYPE).constructor().invokeBasic(type, form, (float) x); case D_TYPE: - return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(D_TYPE).constructor[0].invokeBasic(type, form, (double) x); + return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(D_TYPE).constructor().invokeBasic(type, form, (double) x); default : throw newInternalError("unexpected xtype: " + xtype); } } catch (Throwable t) { @@ -139,8 +139,8 @@ /*non-public*/ abstract int fieldCount(); @Override - final Object internalProperties() { - return "/BMH="+internalValues(); + Object internalProperties() { + return "\n& BMH="+internalValues(); } @Override @@ -219,7 +219,7 @@ @Override /*non-public*/ final BoundMethodHandle copyWithExtendL(MethodType mt, LambdaForm lf, Object narg) { try { - return (BoundMethodHandle) SPECIES_DATA.extendWith(L_TYPE).constructor[0].invokeBasic(mt, lf, argL0, narg); + return (BoundMethodHandle) SPECIES_DATA.extendWith(L_TYPE).constructor().invokeBasic(mt, lf, argL0, narg); } catch (Throwable ex) { throw uncaughtException(ex); } @@ -227,7 +227,7 @@ @Override /*non-public*/ final BoundMethodHandle copyWithExtendI(MethodType mt, LambdaForm lf, int narg) { try { - return (BoundMethodHandle) SPECIES_DATA.extendWith(I_TYPE).constructor[0].invokeBasic(mt, lf, argL0, narg); + return (BoundMethodHandle) SPECIES_DATA.extendWith(I_TYPE).constructor().invokeBasic(mt, lf, argL0, narg); } catch (Throwable ex) { throw uncaughtException(ex); } @@ -235,7 +235,7 @@ @Override /*non-public*/ final BoundMethodHandle copyWithExtendJ(MethodType mt, LambdaForm lf, long narg) { try { - return (BoundMethodHandle) SPECIES_DATA.extendWith(J_TYPE).constructor[0].invokeBasic(mt, lf, argL0, narg); + return (BoundMethodHandle) SPECIES_DATA.extendWith(J_TYPE).constructor().invokeBasic(mt, lf, argL0, narg); } catch (Throwable ex) { throw uncaughtException(ex); } @@ -243,7 +243,7 @@ @Override /*non-public*/ final BoundMethodHandle copyWithExtendF(MethodType mt, LambdaForm lf, float narg) { try { - return (BoundMethodHandle) SPECIES_DATA.extendWith(F_TYPE).constructor[0].invokeBasic(mt, lf, argL0, narg); + return (BoundMethodHandle) SPECIES_DATA.extendWith(F_TYPE).constructor().invokeBasic(mt, lf, argL0, narg); } catch (Throwable ex) { throw uncaughtException(ex); } @@ -251,7 +251,7 @@ @Override /*non-public*/ final BoundMethodHandle copyWithExtendD(MethodType mt, LambdaForm lf, double narg) { try { - return (BoundMethodHandle) SPECIES_DATA.extendWith(D_TYPE).constructor[0].invokeBasic(mt, lf, argL0, narg); + return (BoundMethodHandle) SPECIES_DATA.extendWith(D_TYPE).constructor().invokeBasic(mt, lf, argL0, narg); } catch (Throwable ex) { throw uncaughtException(ex); } @@ -268,18 +268,20 @@ * The fields are immutable; their values are fully specified at object construction. * Each BMH type supplies an array of getter functions which may be used in lambda forms. * A BMH is constructed by cloning a shorter BMH and adding one or more new field values. - * As a degenerate and common case, the "shorter BMH" can be missing, and contributes zero prior fields. + * The shortest possible BMH has zero fields; its class is SimpleMethodHandle. + * BMH species are not interrelated by subtyping, even though it would appear that + * a shorter BMH could serve as a supertype of a longer one which extends it. */ static class SpeciesData { - final String typeChars; - final BasicType[] typeCodes; - final Class clazz; + private final String typeChars; + private final BasicType[] typeCodes; + private final Class clazz; // Bootstrapping requires circular relations MH -> BMH -> SpeciesData -> MH // Therefore, we need a non-final link in the chain. Use array elements. - final MethodHandle[] constructor; - final MethodHandle[] getters; - final NamedFunction[] nominalGetters; - final SpeciesData[] extensions; + @Stable private final MethodHandle[] constructor; + @Stable private final MethodHandle[] getters; + @Stable private final NamedFunction[] nominalGetters; + @Stable private final SpeciesData[] extensions; /*non-public*/ int fieldCount() { return typeCodes.length; @@ -290,9 +292,14 @@ /*non-public*/ char fieldTypeChar(int i) { return typeChars.charAt(i); } - + Object fieldSignature() { + return typeChars; + } + public Class fieldHolder() { + return clazz; + } public String toString() { - return "SpeciesData["+(isPlaceholder() ? "" : clazz.getSimpleName())+":"+typeChars+"]"; + return "SpeciesData<"+fieldSignature()+">"; } /** @@ -301,7 +308,20 @@ * getter. */ NamedFunction getterFunction(int i) { - return nominalGetters[i]; + NamedFunction nf = nominalGetters[i]; + assert(nf.memberDeclaringClassOrNull() == fieldHolder()); + assert(nf.returnType() == fieldType(i)); + return nf; + } + + NamedFunction[] getterFunctions() { + return nominalGetters; + } + + MethodHandle[] getterHandles() { return getters; } + + MethodHandle constructor() { + return constructor[0]; } static final SpeciesData EMPTY = new SpeciesData("", BoundMethodHandle.class); @@ -324,7 +344,7 @@ private void initForBootstrap() { assert(!INIT_DONE); - if (constructor[0] == null) { + if (constructor() == null) { String types = typeChars; Factory.makeCtors(clazz, types, this.constructor); Factory.makeGetters(clazz, types, this.getters); @@ -508,19 +528,19 @@ * return new Species_LLI(mt, lf, argL0, argL1, argI2); * } * final BoundMethodHandle copyWithExtendL(MethodType mt, LambdaForm lf, Object narg) { - * return SPECIES_DATA.extendWith(L_TYPE).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg); + * return SPECIES_DATA.extendWith(L_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg); * } * final BoundMethodHandle copyWithExtendI(MethodType mt, LambdaForm lf, int narg) { - * return SPECIES_DATA.extendWith(I_TYPE).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg); + * return SPECIES_DATA.extendWith(I_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg); * } * final BoundMethodHandle copyWithExtendJ(MethodType mt, LambdaForm lf, long narg) { - * return SPECIES_DATA.extendWith(J_TYPE).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg); + * return SPECIES_DATA.extendWith(J_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg); * } * final BoundMethodHandle copyWithExtendF(MethodType mt, LambdaForm lf, float narg) { - * return SPECIES_DATA.extendWith(F_TYPE).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg); + * return SPECIES_DATA.extendWith(F_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg); * } * public final BoundMethodHandle copyWithExtendD(MethodType mt, LambdaForm lf, double narg) { - * return SPECIES_DATA.extendWith(D_TYPE).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg); + * return SPECIES_DATA.extendWith(D_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg); * } * } * @@ -653,16 +673,14 @@ char btChar = type.basicTypeChar(); mv = cw.visitMethod(NOT_ACC_PUBLIC + ACC_FINAL, "copyWithExtend" + btChar, makeSignature(String.valueOf(btChar), false), null, E_THROWABLE); mv.visitCode(); - // return SPECIES_DATA.extendWith(t).constructor[0].invokeBasic(mt, lf, argL0, ..., narg) + // return SPECIES_DATA.extendWith(t).constructor().invokeBasic(mt, lf, argL0, ..., narg) // obtain constructor mv.visitFieldInsn(GETSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG); int iconstInsn = ICONST_0 + ord; assert(iconstInsn <= ICONST_5); mv.visitInsn(iconstInsn); mv.visitMethodInsn(INVOKEVIRTUAL, SPECIES_DATA, "extendWith", BMHSPECIES_DATA_EWI_SIG, false); - mv.visitFieldInsn(GETFIELD, SPECIES_DATA, "constructor", "[" + MH_SIG); - mv.visitInsn(ICONST_0); - mv.visitInsn(AALOAD); + mv.visitMethodInsn(INVOKEVIRTUAL, SPECIES_DATA, "constructor", "()" + MH_SIG, false); // load mt, lf mv.visitVarInsn(ALOAD, 1); mv.visitVarInsn(ALOAD, 2); diff -r 3bbb6a284bd4 -r d69abed3a07d jdk/src/java.base/share/classes/java/lang/invoke/CallSite.java --- a/jdk/src/java.base/share/classes/java/lang/invoke/CallSite.java Wed Sep 10 19:19:47 2014 +0400 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/CallSite.java Wed Sep 10 19:19:47 2014 +0400 @@ -319,7 +319,7 @@ throw new ClassCastException("bootstrap method failed to produce a CallSite"); } if (!site.getTarget().type().equals(type)) - throw new WrongMethodTypeException("wrong type: "+site.getTarget()); + throw wrongTargetType(site.getTarget(), type); } catch (Throwable ex) { BootstrapMethodError bex; if (ex instanceof BootstrapMethodError) diff -r 3bbb6a284bd4 -r d69abed3a07d jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java --- a/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java Wed Sep 10 19:19:47 2014 +0400 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java Wed Sep 10 19:19:47 2014 +0400 @@ -59,6 +59,7 @@ MemberName m = new MemberName(Object.class, member.getName(), member.getMethodType(), member.getReferenceKind()); m = MemberName.getFactory().resolveOrNull(m.getReferenceKind(), m, null); if (m != null && m.isPublic()) { + assert(member.getReferenceKind() == m.getReferenceKind()); // else this.form is wrong member = m; } } @@ -127,7 +128,7 @@ @Override String internalProperties() { - return "/DMH="+member.toString(); + return "\n& DMH.MN="+internalMemberName(); } //// Implementation methods. diff -r 3bbb6a284bd4 -r d69abed3a07d jdk/src/java.base/share/classes/java/lang/invoke/Invokers.java --- a/jdk/src/java.base/share/classes/java/lang/invoke/Invokers.java Wed Sep 10 19:19:47 2014 +0400 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/Invokers.java Wed Sep 10 19:19:47 2014 +0400 @@ -308,7 +308,7 @@ : Arrays.asList(mtype, customized, which, nameCursor, names.length); if (MTYPE_ARG >= INARG_LIMIT) { assert(names[MTYPE_ARG] == null); - NamedFunction getter = BoundMethodHandle.getSpeciesData("L").getterFunction(0); + NamedFunction getter = BoundMethodHandle.speciesData_L().getterFunction(0); names[MTYPE_ARG] = new Name(getter, names[THIS_MH]); // else if isLinker, then MTYPE is passed in from the caller (e.g., the JVM) } @@ -360,9 +360,6 @@ Object checkGenericType(Object mhObj, Object expectedObj) { MethodHandle mh = (MethodHandle) mhObj; MethodType expected = (MethodType) expectedObj; - if (mh.type() == expected) return mh; - MethodHandle atc = mh.asTypeCache; - if (atc != null && atc.type() == expected) return atc; return mh.asType(expected); /* Maybe add more paths here. Possible optimizations: * for (R)MH.invoke(a*), @@ -436,24 +433,25 @@ } // Local constant functions: - private static final NamedFunction NF_checkExactType; - private static final NamedFunction NF_checkGenericType; - private static final NamedFunction NF_asType; - private static final NamedFunction NF_getCallSiteTarget; + private static final NamedFunction + NF_checkExactType, + NF_checkGenericType, + NF_getCallSiteTarget; static { try { - NF_checkExactType = new NamedFunction(Invokers.class - .getDeclaredMethod("checkExactType", Object.class, Object.class)); - NF_checkGenericType = new NamedFunction(Invokers.class - .getDeclaredMethod("checkGenericType", Object.class, Object.class)); - NF_asType = new NamedFunction(MethodHandle.class - .getDeclaredMethod("asType", MethodType.class)); - NF_getCallSiteTarget = new NamedFunction(Invokers.class - .getDeclaredMethod("getCallSiteTarget", Object.class)); - NF_checkExactType.resolve(); - NF_checkGenericType.resolve(); - NF_getCallSiteTarget.resolve(); - // bound + NamedFunction nfs[] = { + NF_checkExactType = new NamedFunction(Invokers.class + .getDeclaredMethod("checkExactType", Object.class, Object.class)), + NF_checkGenericType = new NamedFunction(Invokers.class + .getDeclaredMethod("checkGenericType", Object.class, Object.class)), + NF_getCallSiteTarget = new NamedFunction(Invokers.class + .getDeclaredMethod("getCallSiteTarget", Object.class)) + }; + for (NamedFunction nf : nfs) { + // Each nf must be statically invocable or we get tied up in our bootstraps. + assert(InvokerBytecodeGenerator.isStaticallyInvocable(nf.member)) : nf; + nf.resolve(); + } } catch (ReflectiveOperationException ex) { throw newInternalError(ex); } diff -r 3bbb6a284bd4 -r d69abed3a07d jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java --- a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java Wed Sep 10 19:19:47 2014 +0400 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java Wed Sep 10 19:19:47 2014 +0400 @@ -243,7 +243,12 @@ this.result = fixResult(result, names); this.names = names.clone(); this.debugName = fixDebugName(debugName); - normalize(); + int maxOutArity = normalize(); + if (maxOutArity > MethodType.MAX_MH_INVOKER_ARITY) { + // Cannot use LF interpreter on very high arity expressions. + assert(maxOutArity <= MethodType.MAX_JVM_ARITY); + compileToBytecode(); + } } LambdaForm(String debugName, @@ -348,9 +353,12 @@ return true; } - /** Renumber and/or replace params so that they are interned and canonically numbered. */ - private void normalize() { + /** Renumber and/or replace params so that they are interned and canonically numbered. + * @return maximum argument list length among the names (since we have to pass over them anyway) + */ + private int normalize() { Name[] oldNames = null; + int maxOutArity = 0; int changesStart = 0; for (int i = 0; i < names.length; i++) { Name n = names[i]; @@ -361,6 +369,8 @@ } names[i] = n.cloneWithIndex(i); } + if (n.arguments != null && maxOutArity < n.arguments.length) + maxOutArity = n.arguments.length; } if (oldNames != null) { int startFixing = arity; @@ -387,6 +397,7 @@ } assert(nameRefsAreLegal()); } + return maxOutArity; } /** @@ -439,8 +450,15 @@ /** Report the N-th argument type. */ BasicType parameterType(int n) { + return parameter(n).type; + } + + /** Report the N-th argument name. */ + Name parameter(int n) { assert(n < arity); - return names[n].type; + Name param = names[n]; + assert(param.isParam()); + return param; } /** Report the arity. */ @@ -582,21 +600,12 @@ isCompiled = true; return vmentry; } catch (Error | Exception ex) { - throw newInternalError("compileToBytecode: " + this, ex); + throw newInternalError(this.toString(), ex); } } - private static final ConcurrentHashMap PREPARED_FORMS; - static { - int capacity = 512; // expect many distinct signatures over time - float loadFactor = 0.75f; // normal default - int writers = 1; - PREPARED_FORMS = new ConcurrentHashMap<>(capacity, loadFactor, writers); - } - - private static Map computeInitialPreparedForms() { + private static void computeInitialPreparedForms() { // Find all predefined invokers and associate them with canonical empty lambda forms. - HashMap forms = new HashMap<>(); for (MemberName m : MemberName.getFactory().getMethods(LambdaForm.class, false, null, null, null)) { if (!m.isStatic() || !m.isPackage()) continue; MethodType mt = m.getMethodType(); @@ -607,13 +616,9 @@ assert(m.getName().equals("interpret" + sig.substring(sig.indexOf('_')))); LambdaForm form = new LambdaForm(sig); form.vmentry = m; - form = mt.form().setCachedLambdaForm(MethodTypeForm.LF_COUNTER, form); - // FIXME: get rid of PREPARED_FORMS; use MethodTypeForm cache only - forms.put(sig, form); + form = mt.form().setCachedLambdaForm(MethodTypeForm.LF_INTERPRET, form); } } - //System.out.println("computeInitialPreparedForms => "+forms); - return forms; } // Set this false to disable use of the interpret_L methods defined in this file. @@ -647,13 +652,11 @@ } private static LambdaForm getPreparedForm(String sig) { MethodType mtype = signatureType(sig); - //LambdaForm prep = PREPARED_FORMS.get(sig); LambdaForm prep = mtype.form().cachedLambdaForm(MethodTypeForm.LF_INTERPRET); if (prep != null) return prep; assert(isValidSignature(sig)); prep = new LambdaForm(sig); prep.vmentry = InvokerBytecodeGenerator.generateLambdaFormInterpreterEntryPoint(sig); - //LambdaForm prep2 = PREPARED_FORMS.putIfAbsent(sig.intern(), prep); return mtype.form().setCachedLambdaForm(MethodTypeForm.LF_INTERPRET, prep); } @@ -709,10 +712,7 @@ /** If the invocation count hits the threshold we spin bytecodes and call that subsequently. */ private static final int COMPILE_THRESHOLD; static { - if (MethodHandleStatics.COMPILE_THRESHOLD != null) - COMPILE_THRESHOLD = MethodHandleStatics.COMPILE_THRESHOLD; - else - COMPILE_THRESHOLD = 30; // default value + COMPILE_THRESHOLD = Math.max(-1, MethodHandleStatics.COMPILE_THRESHOLD); } private int invocationCounter = 0; @@ -728,7 +728,9 @@ for (int i = argumentValues.length; i < values.length; i++) { values[i] = interpretName(names[i], values); } - return (result < 0) ? null : values[result]; + Object rv = (result < 0) ? null : values[result]; + assert(resultCheck(argumentValues, rv)); + return rv; } @Hidden @@ -819,8 +821,16 @@ assert(argumentValues.length == arity) : arity+"!="+Arrays.asList(argumentValues)+".length"; // also check that the leading (receiver) argument is somehow bound to this LF: assert(argumentValues[0] instanceof MethodHandle) : "not MH: " + argumentValues[0]; - assert(((MethodHandle)argumentValues[0]).internalForm() == this); + MethodHandle mh = (MethodHandle) argumentValues[0]; + assert(mh.internalForm() == this); // note: argument #0 could also be an interface wrapper, in the future + argumentTypesMatch(basicTypeSignature(), argumentValues); + return true; + } + private boolean resultCheck(Object[] argumentValues, Object result) { + MethodHandle mh = (MethodHandle) argumentValues[0]; + MethodType mt = mh.type(); + assert(valueMatches(returnType(), mt.returnType(), result)); return true; } @@ -839,7 +849,7 @@ if (i == arity) buf.append(")=>{"); Name n = names[i]; if (i >= arity) buf.append("\n "); - buf.append(n); + buf.append(n.paramString()); if (i < arity) { if (i+1 < arity) buf.append(","); continue; @@ -847,6 +857,7 @@ buf.append("=").append(n.exprString()); buf.append(";"); } + if (arity == names.length) buf.append(")=>{"); buf.append(result < 0 ? "void" : names[result]).append("}"); if (TRACE_INTERPRETER) { // Extra verbosity: @@ -856,53 +867,16 @@ return buf.toString(); } - /** - * Apply immediate binding for a Name in this form indicated by its position relative to the form. - * The first parameter to a LambdaForm, a0:L, always represents the form's method handle, so 0 is not - * accepted as valid. - */ - LambdaForm bindImmediate(int pos, BasicType basicType, Object value) { - // must be an argument, and the types must match - assert pos > 0 && pos < arity && names[pos].type == basicType && Name.typesMatch(basicType, value); - - int arity2 = arity - 1; - Name[] names2 = new Name[names.length - 1]; - for (int r = 0, w = 0; r < names.length; ++r, ++w) { // (r)ead from names, (w)rite to names2 - Name n = names[r]; - if (n.isParam()) { - if (n.index == pos) { - // do not copy over the argument that is to be replaced with a literal, - // but adjust the write index - --w; - } else { - names2[w] = new Name(w, n.type); - } - } else { - Object[] arguments2 = new Object[n.arguments.length]; - for (int i = 0; i < n.arguments.length; ++i) { - Object arg = n.arguments[i]; - if (arg instanceof Name) { - int ni = ((Name) arg).index; - if (ni == pos) { - arguments2[i] = value; - } else if (ni < pos) { - // replacement position not yet passed - arguments2[i] = names2[ni]; - } else { - // replacement position passed - arguments2[i] = names2[ni - 1]; - } - } else { - arguments2[i] = arg; - } - } - names2[w] = new Name(n.function, arguments2); - names2[w].initIndex(w); - } - } - - int result2 = result == -1 ? -1 : result - 1; - return new LambdaForm(debugName, arity2, names2, result2); + @Override + public boolean equals(Object obj) { + return obj instanceof LambdaForm && equals((LambdaForm)obj); + } + public boolean equals(LambdaForm that) { + if (this.result != that.result) return false; + return Arrays.equals(this.names, that.names); + } + public int hashCode() { + return result + 31 * Arrays.hashCode(names); } LambdaForm bind(int namePos, BoundMethodHandle.SpeciesData oldData) { @@ -918,8 +892,8 @@ assert(!binding.isParam()); assert(name.type == binding.type); assert(0 <= pos && pos < arity && names[pos] == name); - assert(binding.function.memberDeclaringClassOrNull() == newData.clazz); - assert(oldData.getters.length == newData.getters.length-1); + assert(binding.function.memberDeclaringClassOrNull() == newData.fieldHolder()); + assert(oldData.getterFunctions().length == newData.getterFunctions().length-1); if (bindCache != null) { LambdaForm form = bindCache[pos]; if (form != null) { @@ -940,12 +914,12 @@ for (int i = 0; i < names2.length; i++) { Name n = names[i]; if (n.function != null && - n.function.memberDeclaringClassOrNull() == oldData.clazz) { + n.function.memberDeclaringClassOrNull() == oldData.fieldHolder()) { MethodHandle oldGetter = n.function.resolvedHandle; MethodHandle newGetter = null; - for (int j = 0; j < oldData.getters.length; j++) { - if (oldGetter == oldData.getters[j]) - newGetter = newData.getters[j]; + for (int j = 0; j < oldData.getterHandles().length; j++) { + if (oldGetter == oldData.getterHandles()[j]) + newGetter = newData.getterHandles()[j]; } if (newGetter != null) { if (firstOldRef < 0) firstOldRef = i; @@ -969,7 +943,7 @@ int insPos = pos; for (; insPos+1 < names2.length; insPos++) { Name n = names2[insPos+1]; - if (n.isSiblingBindingBefore(binding)) { + if (n.isParam()) { names2[insPos] = n; } else { break; @@ -1000,16 +974,16 @@ } LambdaForm addArguments(int pos, BasicType... types) { - assert(pos <= arity); + // names array has MH in slot 0; skip it. + int argpos = pos + 1; + assert(argpos <= arity); int length = names.length; int inTypes = types.length; Name[] names2 = Arrays.copyOf(names, length + inTypes); int arity2 = arity + inTypes; int result2 = result; - if (result2 >= arity) + if (result2 >= argpos) result2 += inTypes; - // names array has MH in slot 0; skip it. - int argpos = pos + 1; // Note: The LF constructor will rename names2[argpos...]. // Make space for new arguments (shift temporaries). System.arraycopy(names, argpos, names2, argpos + inTypes, length - argpos); @@ -1102,8 +1076,9 @@ } NamedFunction(MemberName member, MethodHandle resolvedHandle) { this.member = member; - //resolvedHandle = eraseSubwordTypes(resolvedHandle); this.resolvedHandle = resolvedHandle; + // The following assert is almost always correct, but will fail for corner cases, such as PrivateInvokeTest. + //assert(!isInvokeBasic()); } NamedFunction(MethodType basicInvokerType) { assert(basicInvokerType == basicInvokerType.basicType()) : basicInvokerType; @@ -1114,6 +1089,13 @@ // necessary to pass BigArityTest this.member = Invokers.invokeBasicMethod(basicInvokerType); } + assert(isInvokeBasic()); + } + + private boolean isInvokeBasic() { + return member != null && + member.isMethodHandleInvoke() && + "invokeBasic".equals(member.getName()); } // The next 3 constructors are used to break circular dependencies on MH.invokeStatic, etc. @@ -1179,71 +1161,89 @@ /** void return type invokers. */ @Hidden static Object invoke__V(MethodHandle mh, Object[] a) throws Throwable { - assert(a.length == 0); + assert(arityCheck(0, void.class, mh, a)); mh.invokeBasic(); return null; } @Hidden static Object invoke_L_V(MethodHandle mh, Object[] a) throws Throwable { - assert(a.length == 1); + assert(arityCheck(1, void.class, mh, a)); mh.invokeBasic(a[0]); return null; } @Hidden static Object invoke_LL_V(MethodHandle mh, Object[] a) throws Throwable { - assert(a.length == 2); + assert(arityCheck(2, void.class, mh, a)); mh.invokeBasic(a[0], a[1]); return null; } @Hidden static Object invoke_LLL_V(MethodHandle mh, Object[] a) throws Throwable { - assert(a.length == 3); + assert(arityCheck(3, void.class, mh, a)); mh.invokeBasic(a[0], a[1], a[2]); return null; } @Hidden static Object invoke_LLLL_V(MethodHandle mh, Object[] a) throws Throwable { - assert(a.length == 4); + assert(arityCheck(4, void.class, mh, a)); mh.invokeBasic(a[0], a[1], a[2], a[3]); return null; } @Hidden static Object invoke_LLLLL_V(MethodHandle mh, Object[] a) throws Throwable { - assert(a.length == 5); + assert(arityCheck(5, void.class, mh, a)); mh.invokeBasic(a[0], a[1], a[2], a[3], a[4]); return null; } /** Object return type invokers. */ @Hidden static Object invoke__L(MethodHandle mh, Object[] a) throws Throwable { - assert(a.length == 0); + assert(arityCheck(0, mh, a)); return mh.invokeBasic(); } @Hidden static Object invoke_L_L(MethodHandle mh, Object[] a) throws Throwable { - assert(a.length == 1); + assert(arityCheck(1, mh, a)); return mh.invokeBasic(a[0]); } @Hidden static Object invoke_LL_L(MethodHandle mh, Object[] a) throws Throwable { - assert(a.length == 2); + assert(arityCheck(2, mh, a)); return mh.invokeBasic(a[0], a[1]); } @Hidden static Object invoke_LLL_L(MethodHandle mh, Object[] a) throws Throwable { - assert(a.length == 3); + assert(arityCheck(3, mh, a)); return mh.invokeBasic(a[0], a[1], a[2]); } @Hidden static Object invoke_LLLL_L(MethodHandle mh, Object[] a) throws Throwable { - assert(a.length == 4); + assert(arityCheck(4, mh, a)); return mh.invokeBasic(a[0], a[1], a[2], a[3]); } @Hidden static Object invoke_LLLLL_L(MethodHandle mh, Object[] a) throws Throwable { - assert(a.length == 5); + assert(arityCheck(5, mh, a)); return mh.invokeBasic(a[0], a[1], a[2], a[3], a[4]); } + private static boolean arityCheck(int arity, MethodHandle mh, Object[] a) { + return arityCheck(arity, Object.class, mh, a); + } + private static boolean arityCheck(int arity, Class rtype, MethodHandle mh, Object[] a) { + assert(a.length == arity) + : Arrays.asList(a.length, arity); + assert(mh.type().basicType() == MethodType.genericMethodType(arity).changeReturnType(rtype)) + : Arrays.asList(mh, rtype, arity); + MemberName member = mh.internalMemberName(); + if (member != null && member.getName().equals("invokeBasic") && member.isMethodHandleInvoke()) { + assert(arity > 0); + assert(a[0] instanceof MethodHandle); + MethodHandle mh2 = (MethodHandle) a[0]; + assert(mh2.type().basicType() == MethodType.genericMethodType(arity-1).changeReturnType(rtype)) + : Arrays.asList(member, mh2, rtype, arity); + } + return true; + } static final MethodType INVOKER_METHOD_TYPE = MethodType.methodType(Object.class, MethodHandle.class, Object[].class); @@ -1431,7 +1431,7 @@ this(new NamedFunction(function), arguments); } Name(NamedFunction function, Object... arguments) { - this(-1, function.returnType(), function, arguments = arguments.clone()); + this(-1, function.returnType(), function, arguments = Arrays.copyOf(arguments, arguments.length, Object[].class)); assert(arguments.length == function.arity()) : "arity mismatch: arguments.length=" + arguments.length + " == function.arity()=" + function.arity() + " in " + debugString(); for (int i = 0; i < arguments.length; i++) assert(typesMatch(function.parameterType(i), arguments[i])) : "types don't match: function.parameterType(" + i + ")=" + function.parameterType(i) + ", arguments[" + i + "]=" + arguments[i] + " in " + debugString(); @@ -1487,7 +1487,11 @@ if (!replaced) return this; return new Name(function, arguments); } + /** In the arguments of this Name, replace oldNames[i] pairwise by newNames[i]. + * Limit such replacements to {@code start<=i= end) return this; @SuppressWarnings("LocalVariableHidesMemberVariable") Object[] arguments = this.arguments; boolean replaced = false; @@ -1539,9 +1543,12 @@ return (isParam()?"a":"t")+(index >= 0 ? index : System.identityHashCode(this))+":"+typeChar(); } public String debugString() { - String s = toString(); + String s = paramString(); return (function == null) ? s : s + "=" + exprString(); } + public String paramString() { + return toString(); + } public String exprString() { if (function == null) return toString(); StringBuilder buf = new StringBuilder(function.toString()); @@ -1572,34 +1579,6 @@ return true; } - /** - * Does this Name precede the given binding node in some canonical order? - * This predicate is used to order data bindings (via insertion sort) - * with some stability. - */ - boolean isSiblingBindingBefore(Name binding) { - assert(!binding.isParam()); - if (isParam()) return true; - if (function.equals(binding.function) && - arguments.length == binding.arguments.length) { - boolean sawInt = false; - for (int i = 0; i < arguments.length; i++) { - Object a1 = arguments[i]; - Object a2 = binding.arguments[i]; - if (!a1.equals(a2)) { - if (a1 instanceof Integer && a2 instanceof Integer) { - if (sawInt) continue; - sawInt = true; - if ((int)a1 < (int)a2) continue; // still might be true - } - return false; - } - } - return sawInt; - } - return false; - } - /** Return the index of the last occurrence of n in the argument array. * Return -1 if the name is not used. */ @@ -1858,37 +1837,6 @@ @interface Hidden { } - -/* - // Smoke-test for the invokers used in this file. - static void testMethodHandleLinkers() throws Throwable { - MemberName.Factory lookup = MemberName.getFactory(); - MemberName asList_MN = new MemberName(Arrays.class, "asList", - MethodType.methodType(List.class, Object[].class), - REF_invokeStatic); - //MethodHandleNatives.resolve(asList_MN, null); - asList_MN = lookup.resolveOrFail(asList_MN, REF_invokeStatic, null, NoSuchMethodException.class); - System.out.println("about to call "+asList_MN); - Object[] abc = { "a", "bc" }; - List lst = (List) MethodHandle.linkToStatic(abc, asList_MN); - System.out.println("lst="+lst); - MemberName toString_MN = new MemberName(Object.class.getMethod("toString")); - String s1 = (String) MethodHandle.linkToVirtual(lst, toString_MN); - toString_MN = new MemberName(Object.class.getMethod("toString"), true); - String s2 = (String) MethodHandle.linkToSpecial(lst, toString_MN); - System.out.println("[s1,s2,lst]="+Arrays.asList(s1, s2, lst.toString())); - MemberName toArray_MN = new MemberName(List.class.getMethod("toArray")); - Object[] arr = (Object[]) MethodHandle.linkToInterface(lst, toArray_MN); - System.out.println("toArray="+Arrays.toString(arr)); - } - static { try { testMethodHandleLinkers(); } catch (Throwable ex) { throw new RuntimeException(ex); } } - // Requires these definitions in MethodHandle: - static final native Object linkToStatic(Object x1, MemberName mn) throws Throwable; - static final native Object linkToVirtual(Object x1, MemberName mn) throws Throwable; - static final native Object linkToSpecial(Object x1, MemberName mn) throws Throwable; - static final native Object linkToInterface(Object x1, MemberName mn) throws Throwable; - */ - private static final HashMap DEBUG_NAME_COUNTERS; static { if (debugEnabled()) @@ -1901,7 +1849,7 @@ static { createIdentityForms(); if (USE_PREDEFINED_INTERPRET_METHODS) - PREPARED_FORMS.putAll(computeInitialPreparedForms()); + computeInitialPreparedForms(); NamedFunction.initializeInvokers(); } diff -r 3bbb6a284bd4 -r d69abed3a07d jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java --- a/jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java Wed Sep 10 19:19:47 2014 +0400 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java Wed Sep 10 19:19:47 2014 +0400 @@ -327,10 +327,6 @@ assert(getReferenceKind() == oldKind); assert(MethodHandleNatives.refKindIsValid(refKind)); flags += (((int)refKind - oldKind) << MN_REFERENCE_KIND_SHIFT); -// if (isConstructor() && refKind != REF_newInvokeSpecial) -// flags += (IS_METHOD - IS_CONSTRUCTOR); -// else if (refKind == REF_newInvokeSpecial && isMethod()) -// flags += (IS_CONSTRUCTOR - IS_METHOD); return this; } @@ -344,9 +340,11 @@ return !testFlags(mask, 0); } - /** Utility method to query if this member is a method handle invocation (invoke or invokeExact). */ + /** Utility method to query if this member is a method handle invocation (invoke or invokeExact). + * Also returns true for the non-public MH.invokeBasic. + */ public boolean isMethodHandleInvoke() { - final int bits = MH_INVOKE_MODS; + final int bits = MH_INVOKE_MODS &~ Modifier.PUBLIC; final int negs = Modifier.STATIC; if (testFlags(bits | negs, bits) && clazz == MethodHandle.class) { @@ -355,7 +353,14 @@ return false; } public static boolean isMethodHandleInvokeName(String name) { - return name.equals("invoke") || name.equals("invokeExact"); + switch (name) { + case "invoke": + case "invokeExact": + case "invokeBasic": // internal sig-poly method + return true; + default: + return false; + } } private static final int MH_INVOKE_MODS = Modifier.NATIVE | Modifier.FINAL | Modifier.PUBLIC; @@ -720,16 +725,8 @@ init(defClass, name, type, flagsMods(IS_FIELD, 0, refKind)); initResolved(false); } - /** Create a field or type name from the given components: Declaring class, name, type. - * The declaring class may be supplied as null if this is to be a bare name and type. - * The modifier flags default to zero. - * The resulting name will in an unresolved state. - */ - public MemberName(Class defClass, String name, Class type, Void unused) { - this(defClass, name, type, REF_NONE); - initResolved(false); - } - /** Create a method or constructor name from the given components: Declaring class, name, type, modifiers. + /** Create a method or constructor name from the given components: + * Declaring class, name, type, reference kind. * It will be a constructor if and only if the name is {@code "<init>"}. * The declaring class may be supplied as null if this is to be a bare name and type. * The last argument is optional, a boolean which requests REF_invokeSpecial. diff -r 3bbb6a284bd4 -r d69abed3a07d jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java --- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java Wed Sep 10 19:19:47 2014 +0400 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java Wed Sep 10 19:19:47 2014 +0400 @@ -762,11 +762,19 @@ return this; } // Return 'this.asTypeCache' if the conversion is already memoized. + MethodHandle atc = asTypeCached(newType); + if (atc != null) { + return atc; + } + return asTypeUncached(newType); + } + + private MethodHandle asTypeCached(MethodType newType) { MethodHandle atc = asTypeCache; if (atc != null && newType == atc.type) { return atc; } - return asTypeUncached(newType); + return null; } /** Override this to change asType behavior. */ @@ -991,8 +999,11 @@ return MethodHandles.collectArguments(target, collectArgPos, collector); } - // private API: return true if last param exactly matches arrayType - private boolean asCollectorChecks(Class arrayType, int arrayLength) { + /** + * See if {@code asCollector} can be validly called with the given arguments. + * Return false if the last parameter is not an exact match to arrayType. + */ + /*non-public*/ boolean asCollectorChecks(Class arrayType, int arrayLength) { spreadArrayChecks(arrayType, arrayLength); int nargs = type().parameterCount(); if (nargs != 0) { @@ -1154,7 +1165,7 @@ * @see #asFixedArity */ public MethodHandle asVarargsCollector(Class arrayType) { - Class arrayElement = arrayType.getComponentType(); + arrayType.getClass(); // explicit NPE boolean lastMatch = asCollectorChecks(arrayType, 0); if (isVarargsCollector() && lastMatch) return this; @@ -1283,14 +1294,17 @@ */ @Override public String toString() { - if (DEBUG_METHOD_HANDLE_NAMES) return debugString(); + if (DEBUG_METHOD_HANDLE_NAMES) return "MethodHandle"+debugString(); return standardString(); } String standardString() { return "MethodHandle"+type; } + /** Return a string with a several lines describing the method handle structure. + * This string would be suitable for display in an IDE debugger. + */ String debugString() { - return standardString()+"/LF="+internalForm()+internalProperties(); + return type+" : "+internalForm()+internalProperties(); } //// Implementation methods. @@ -1302,15 +1316,13 @@ /*non-public*/ MethodHandle setVarargs(MemberName member) throws IllegalAccessException { if (!member.isVarargs()) return this; - int argc = type().parameterCount(); - if (argc != 0) { - Class arrayType = type().parameterType(argc-1); - if (arrayType.isArray()) { - return MethodHandleImpl.makeVarargsCollector(this, arrayType); - } + Class arrayType = type().lastParameterType(); + if (arrayType.isArray()) { + return MethodHandleImpl.makeVarargsCollector(this, arrayType); } throw member.makeAccessException("cannot make variable arity", null); } + /*non-public*/ MethodHandle viewAsType(MethodType newType) { // No actual conversions, just a new view of the same method. @@ -1361,7 +1373,7 @@ /*non-public*/ Object internalProperties() { - // Override to something like "/FOO=bar" + // Override to something to follow this.form, like "\n& FOO=bar" return ""; } @@ -1469,6 +1481,7 @@ /*non-public*/ void updateForm(LambdaForm newForm) { if (form == newForm) return; + assert(this instanceof DirectMethodHandle && this.internalMemberName().isStatic()); // ISSUE: Should we have a memory fence here? UNSAFE.putObject(this, FORM_OFFSET, newForm); this.form.prepare(); // as in MethodHandle. diff -r 3bbb6a284bd4 -r d69abed3a07d jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java --- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java Wed Sep 10 19:19:47 2014 +0400 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java Wed Sep 10 19:19:47 2014 +0400 @@ -357,7 +357,7 @@ static class AsVarargsCollector extends MethodHandle { private final MethodHandle target; private final Class arrayType; - private /*@Stable*/ MethodHandle asCollectorCache; + private @Stable MethodHandle asCollectorCache; AsVarargsCollector(MethodHandle target, MethodType type, Class arrayType) { super(type, reinvokerForm(target)); @@ -534,7 +534,6 @@ static final MethodHandle MH_castReference; static final MethodHandle MH_copyAsPrimitiveArray; - static final MethodHandle MH_copyAsReferenceArray; static final MethodHandle MH_fillNewTypedArray; static final MethodHandle MH_fillNewArray; static final MethodHandle MH_arrayIdentity; @@ -557,8 +556,6 @@ MethodType.methodType(Object.class, Class.class, Object.class)); MH_copyAsPrimitiveArray = IMPL_LOOKUP.findStatic(MHI, "copyAsPrimitiveArray", MethodType.methodType(Object.class, Wrapper.class, Object[].class)); - MH_copyAsReferenceArray = IMPL_LOOKUP.findStatic(MHI, "copyAsReferenceArray", - MethodType.methodType(Object[].class, Class.class, Object[].class)); MH_arrayIdentity = IMPL_LOOKUP.findStatic(MHI, "identity", MethodType.methodType(Object[].class, Object[].class)); MH_fillNewArray = IMPL_LOOKUP.findStatic(MHI, "fillNewArray", @@ -758,8 +755,8 @@ BoundMethodHandle mh; try { mh = (BoundMethodHandle) - data.constructor[0].invokeBasic(type, form, (Object) target, (Object) exType, (Object) catcher, - (Object) collectArgs, (Object) unboxResult); + data.constructor().invokeBasic(type, form, (Object) target, (Object) exType, (Object) catcher, + (Object) collectArgs, (Object) unboxResult); } catch (Throwable ex) { throw uncaughtException(ex); } @@ -1095,6 +1092,7 @@ } private static Object[] fillNewTypedArray(Object[] example, Integer len, Object[] /*not ...*/ args) { Object[] a = Arrays.copyOf(example, len); + assert(a.getClass() != Object[].class); fillWithArguments(a, 0, args); return a; } @@ -1143,9 +1141,6 @@ } private static final MethodHandle[] FILL_ARRAYS = makeFillArrays(); - private static Object[] copyAsReferenceArray(Class arrayType, Object... a) { - return Arrays.copyOf(a, a.length, arrayType); - } private static Object copyAsPrimitiveArray(Wrapper w, Object... boxes) { Object a = w.makeArray(boxes.length); w.copyArrayUnboxing(boxes, 0, a, 0, boxes.length); @@ -1265,8 +1260,8 @@ if (nargs >= MAX_JVM_ARITY/2 - 1) { int slots = nargs; final int MAX_ARRAY_SLOTS = MAX_JVM_ARITY - 1; // 1 for receiver MH - if (arrayType == double[].class || arrayType == long[].class) - slots *= 2; + if (slots <= MAX_ARRAY_SLOTS && elemType.isPrimitive()) + slots *= Wrapper.forPrimitiveType(elemType).stackSlots(); if (slots > MAX_ARRAY_SLOTS) throw new IllegalArgumentException("too many arguments: "+arrayType.getSimpleName()+", length "+nargs); } @@ -1276,16 +1271,18 @@ MethodHandle cache[] = TYPED_COLLECTORS.get(elemType); MethodHandle mh = nargs < cache.length ? cache[nargs] : null; if (mh != null) return mh; - if (elemType.isPrimitive()) { + if (nargs == 0) { + Object example = java.lang.reflect.Array.newInstance(arrayType.getComponentType(), 0); + mh = MethodHandles.constant(arrayType, example); + } else if (elemType.isPrimitive()) { MethodHandle builder = Lazy.MH_fillNewArray; MethodHandle producer = buildArrayProducer(arrayType); mh = buildVarargsArray(builder, producer, nargs); } else { - @SuppressWarnings("unchecked") - Class objArrayType = (Class) arrayType; + Class objArrayType = arrayType.asSubclass(Object[].class); Object[] example = Arrays.copyOf(NO_ARGS_ARRAY, 0, objArrayType); MethodHandle builder = Lazy.MH_fillNewTypedArray.bindTo(example); - MethodHandle producer = Lazy.MH_arrayIdentity; + MethodHandle producer = Lazy.MH_arrayIdentity; // must be weakly typed mh = buildVarargsArray(builder, producer, nargs); } mh = mh.asType(MethodType.methodType(arrayType, Collections.>nCopies(nargs, elemType))); @@ -1297,9 +1294,7 @@ private static MethodHandle buildArrayProducer(Class arrayType) { Class elemType = arrayType.getComponentType(); - if (elemType.isPrimitive()) - return Lazy.MH_copyAsPrimitiveArray.bindTo(Wrapper.forPrimitiveType(elemType)); - else - return Lazy.MH_copyAsReferenceArray.bindTo(arrayType); + assert(elemType.isPrimitive()); + return Lazy.MH_copyAsPrimitiveArray.bindTo(Wrapper.forPrimitiveType(elemType)); } } diff -r 3bbb6a284bd4 -r d69abed3a07d jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java --- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java Wed Sep 10 19:19:47 2014 +0400 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java Wed Sep 10 19:19:47 2014 +0400 @@ -33,6 +33,7 @@ import sun.reflect.CallerSensitive; import sun.reflect.Reflection; import sun.reflect.misc.ReflectUtil; +import static java.lang.invoke.MethodHandleStatics.*; /** * This class consists exclusively of static methods that help adapt @@ -148,7 +149,7 @@ public static T asInterfaceInstance(final Class intfc, final MethodHandle target) { if (!intfc.isInterface() || !Modifier.isPublic(intfc.getModifiers())) - throw new IllegalArgumentException("not a public interface: "+intfc.getName()); + throw newIllegalArgumentException("not a public interface", intfc.getName()); final MethodHandle mh; if (System.getSecurityManager() != null) { final Class caller = Reflection.getCallerClass(); @@ -165,7 +166,7 @@ } final Method[] methods = getSingleNameMethods(intfc); if (methods == null) - throw new IllegalArgumentException("not a single-method interface: "+intfc.getName()); + throw newIllegalArgumentException("not a single-method interface", intfc.getName()); final MethodHandle[] vaTargets = new MethodHandle[methods.length]; for (int i = 0; i < methods.length; i++) { Method sm = methods[i]; @@ -189,7 +190,7 @@ return getArg(method.getName()); if (isObjectMethod(method)) return callObjectMethod(proxy, method, args); - throw new InternalError("bad proxy method: "+method); + throw newInternalError("bad proxy method: "+method); } }; @@ -240,7 +241,7 @@ return (WrapperInstance) x; } catch (ClassCastException ex) { } - throw new IllegalArgumentException("not a wrapper instance"); + throw newIllegalArgumentException("not a wrapper instance"); } /** diff -r 3bbb6a284bd4 -r d69abed3a07d jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java --- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java Wed Sep 10 19:19:47 2014 +0400 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java Wed Sep 10 19:19:47 2014 +0400 @@ -45,7 +45,7 @@ static final boolean DUMP_CLASS_FILES; static final boolean TRACE_INTERPRETER; static final boolean TRACE_METHOD_LINKAGE; - static final Integer COMPILE_THRESHOLD; + static final int COMPILE_THRESHOLD; static final int PROFILE_LEVEL; static { @@ -56,7 +56,7 @@ values[1] = Boolean.getBoolean("java.lang.invoke.MethodHandle.DUMP_CLASS_FILES"); values[2] = Boolean.getBoolean("java.lang.invoke.MethodHandle.TRACE_INTERPRETER"); values[3] = Boolean.getBoolean("java.lang.invoke.MethodHandle.TRACE_METHOD_LINKAGE"); - values[4] = Integer.getInteger("java.lang.invoke.MethodHandle.COMPILE_THRESHOLD"); + values[4] = Integer.getInteger("java.lang.invoke.MethodHandle.COMPILE_THRESHOLD", 30); values[5] = Integer.getInteger("java.lang.invoke.MethodHandle.PROFILE_LEVEL", 0); return null; } @@ -131,7 +131,10 @@ /*non-public*/ static RuntimeException newIllegalArgumentException(String message, Object obj, Object obj2) { return new IllegalArgumentException(message(message, obj, obj2)); } + /** Propagate unchecked exceptions and errors, but wrap anything checked and throw that instead. */ /*non-public*/ static Error uncaughtException(Throwable ex) { + if (ex instanceof Error) throw (Error) ex; + if (ex instanceof RuntimeException) throw (RuntimeException) ex; throw newInternalError("uncaught exception", ex); } static Error NYI() { diff -r 3bbb6a284bd4 -r d69abed3a07d jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java --- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Wed Sep 10 19:19:47 2014 +0400 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Wed Sep 10 19:19:47 2014 +0400 @@ -862,6 +862,8 @@ return invoker(type); if ("invokeExact".equals(name)) return exactInvoker(type); + if ("invokeBasic".equals(name)) + return basicInvoker(type); assert(!MemberName.isMethodHandleInvokeName(name)); return null; } @@ -1879,7 +1881,7 @@ static public MethodHandle spreadInvoker(MethodType type, int leadingArgCount) { if (leadingArgCount < 0 || leadingArgCount > type.parameterCount()) - throw new IllegalArgumentException("bad argument count "+leadingArgCount); + throw newIllegalArgumentException("bad argument count", leadingArgCount); return type.invokers().spreadInvoker(leadingArgCount); } @@ -1964,7 +1966,7 @@ static /*non-public*/ MethodHandle basicInvoker(MethodType type) { - return type.form().basicInvoker(); + return type.invokers().basicInvoker(); } /// method handle modification (creation from other method handles) diff -r 3bbb6a284bd4 -r d69abed3a07d jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java --- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java Wed Sep 10 19:19:47 2014 +0400 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java Wed Sep 10 19:19:47 2014 +0400 @@ -905,7 +905,7 @@ if (!descriptor.startsWith("(") || // also generates NPE if needed descriptor.indexOf(')') < 0 || descriptor.indexOf('.') >= 0) - throw new IllegalArgumentException("not a method descriptor: "+descriptor); + throw newIllegalArgumentException("not a method descriptor: "+descriptor); List> types = BytecodeDescriptor.parseMethod(descriptor, loader); Class rtype = types.remove(types.size() - 1); checkSlotCount(types.size()); diff -r 3bbb6a284bd4 -r d69abed3a07d jdk/src/java.base/share/classes/java/lang/invoke/MethodTypeForm.java --- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodTypeForm.java Wed Sep 10 19:19:47 2014 +0400 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodTypeForm.java Wed Sep 10 19:19:47 2014 +0400 @@ -47,14 +47,10 @@ final int[] argToSlotTable, slotToArgTable; final long argCounts; // packed slot & value counts final long primCounts; // packed prim & double counts - final int vmslots; // total number of parameter slots final MethodType erasedType; // the canonical erasure final MethodType basicType; // the canonical erasure, with primitives simplified // Cached adapter information: - @Stable String typeString; // argument type signature characters - @Stable MethodHandle genericInvoker; // JVM hook for inexact invoke - @Stable MethodHandle basicInvoker; // cached instance of MH.invokeBasic @Stable MethodHandle namedFunctionInvoker; // cached helper for LF.NamedFunction // Cached lambda form information, for basic types only: @@ -70,24 +66,40 @@ LF_INTERPRET = 6, // LF interpreter LF_COUNTER = 7, // CMH wrapper LF_REINVOKE = 8, // other wrapper - LF_EX_LINKER = 9, // invokeExact_MT - LF_EX_INVOKER = 10, // invokeExact MH - LF_GEN_LINKER = 11, - LF_GEN_INVOKER = 12, + LF_EX_LINKER = 9, // invokeExact_MT (for invokehandle) + LF_EX_INVOKER = 10, // MHs.invokeExact + LF_GEN_LINKER = 11, // generic invoke_MT (for invokehandle) + LF_GEN_INVOKER = 12, // generic MHs.invoke LF_CS_LINKER = 13, // linkToCallSite_CS LF_MH_LINKER = 14, // linkToCallSite_MH - LF_GWC = 15, + LF_GWC = 15, // guardWithCatch (catchException) LF_LIMIT = 16; + /** Return the type corresponding uniquely (1-1) to this MT-form. + * It might have any primitive returns or arguments, but will have no references except Object. + */ public MethodType erasedType() { return erasedType; } + /** Return the basic type derived from the erased type of this MT-form. + * A basic type is erased (all references Object) and also has all primitive + * types (except int, long, float, double, void) normalized to int. + * Such basic types correspond to low-level JVM calling sequences. + */ public MethodType basicType() { return basicType; } + private boolean assertIsBasicType() { + // primitives must be flattened also + assert(erasedType == basicType) + : "erasedType: " + erasedType + " != basicType: " + basicType; + return true; + } + public LambdaForm cachedLambdaForm(int which) { + assert(assertIsBasicType()); return lambdaForms[which]; } @@ -98,28 +110,6 @@ return lambdaForms[which] = form; } - public MethodHandle basicInvoker() { - assert(erasedType == basicType) : "erasedType: " + erasedType + " != basicType: " + basicType; // primitives must be flattened also - MethodHandle invoker = basicInvoker; - if (invoker != null) return invoker; - invoker = DirectMethodHandle.make(invokeBasicMethod(basicType)); - basicInvoker = invoker; - return invoker; - } - - // This next one is called from LambdaForm.NamedFunction.. - /*non-public*/ static MemberName invokeBasicMethod(MethodType basicType) { - assert(basicType == basicType.basicType()); - try { - // Do approximately the same as this public API call: - // Lookup.findVirtual(MethodHandle.class, name, type); - // But bypass access and corner case checks, since we know exactly what we need. - return IMPL_LOOKUP.resolveOrFail(REF_invokeVirtual, MethodHandle.class, "invokeBasic", basicType); - } catch (ReflectiveOperationException ex) { - throw newInternalError("JVM cannot find invoker for "+basicType, ex); - } - } - /** * Build an MTF for a given type, which must have all references erased to Object. * This MTF will stand for that type and all un-erased variations. @@ -172,6 +162,15 @@ this.basicType = erasedType; } else { this.basicType = MethodType.makeImpl(bt, bpts, true); + // fill in rest of data from the basic type: + MethodTypeForm that = this.basicType.form(); + assert(this != that); + this.primCounts = that.primCounts; + this.argCounts = that.argCounts; + this.argToSlotTable = that.argToSlotTable; + this.slotToArgTable = that.slotToArgTable; + this.lambdaForms = null; + return; } if (lac != 0) { int slot = ptypeCount + lac; @@ -187,10 +186,14 @@ argToSlotTab[1+i] = slot; } assert(slot == 0); // filled the table - } - this.primCounts = pack(lrc, prc, lac, pac); - this.argCounts = pack(rslotCount, rtypeCount, pslotCount, ptypeCount); - if (slotToArgTab == null) { + } else if (pac != 0) { + // have primitives but no long primitives; share slot counts with generic + assert(ptypeCount == pslotCount); + MethodTypeForm that = MethodType.genericMethodType(ptypeCount).form(); + assert(this != that); + slotToArgTab = that.slotToArgTable; + argToSlotTab = that.argToSlotTable; + } else { int slot = ptypeCount; // first arg is deepest in stack slotToArgTab = new int[slot+1]; argToSlotTab = new int[1+ptypeCount]; @@ -201,19 +204,16 @@ argToSlotTab[1+i] = slot; } } + this.primCounts = pack(lrc, prc, lac, pac); + this.argCounts = pack(rslotCount, rtypeCount, pslotCount, ptypeCount); this.argToSlotTable = argToSlotTab; this.slotToArgTable = slotToArgTab; if (pslotCount >= 256) throw newIllegalArgumentException("too many arguments"); - // send a few bits down to the JVM: - this.vmslots = parameterSlotCount(); - - if (basicType == erasedType) { - lambdaForms = new LambdaForm[LF_LIMIT]; - } else { - lambdaForms = null; // could be basicType.form().lambdaForms; - } + // Initialize caches, but only for basic types + assert(basicType == erasedType); + this.lambdaForms = new LambdaForm[LF_LIMIT]; } private static long pack(int a, int b, int c, int d) { @@ -300,7 +300,7 @@ */ public static MethodType canonicalize(MethodType mt, int howRet, int howArgs) { Class[] ptypes = mt.ptypes(); - Class[] ptc = MethodTypeForm.canonicalizes(ptypes, howArgs); + Class[] ptc = MethodTypeForm.canonicalizeAll(ptypes, howArgs); Class rtype = mt.returnType(); Class rtc = MethodTypeForm.canonicalize(rtype, howRet); if (ptc == null && rtc == null) { @@ -368,7 +368,7 @@ /** Canonicalize each param type in the given array. * Return null if all types are already canonicalized. */ - static Class[] canonicalizes(Class[] ts, int how) { + static Class[] canonicalizeAll(Class[] ts, int how) { Class[] cs = null; for (int imax = ts.length, i = 0; i < imax; i++) { Class c = canonicalize(ts[i], how); diff -r 3bbb6a284bd4 -r d69abed3a07d jdk/test/java/lang/invoke/MethodHandlesTest.java --- a/jdk/test/java/lang/invoke/MethodHandlesTest.java Wed Sep 10 19:19:47 2014 +0400 +++ b/jdk/test/java/lang/invoke/MethodHandlesTest.java Wed Sep 10 19:19:47 2014 +0400 @@ -2160,15 +2160,23 @@ else type = type.changeParameterType(j, argType); if (done.add(type)) - testInvokers(type); + testInvokersWithCatch(type); MethodType vtype = type.changeReturnType(void.class); if (done.add(vtype)) - testInvokers(vtype); + testInvokersWithCatch(vtype); } } } } + public void testInvokersWithCatch(MethodType type) throws Throwable { + try { + testInvokers(type); + } catch (Throwable ex) { + System.out.println("*** testInvokers on "+type+" => "); + ex.printStackTrace(System.out); + } + } public void testInvokers(MethodType type) throws Throwable { if (verbosity >= 3) System.out.println("test invokers for "+type);