# HG changeset patch # User redestad # Date 1447689941 -3600 # Mon Nov 16 17:05:41 2015 +0100 # Node ID 8a1177a25a07322ec1d8ec9ea792eabddd311f30 # Parent b39bfadab299c2c5d824402c7d857b4d0187f4df 8142334: Improve lazy initialization of java.lang.invoke Reviewed-by: psandoz, vlivanov, mhaupt diff --git a/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java b/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java --- a/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java +++ b/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java @@ -224,12 +224,12 @@ assert(names.length == nameCursor); if (doesAlloc) { // names = { argx,y,z,... new C, init method } - names[NEW_OBJ] = new Name(Lazy.NF_allocateInstance, names[DMH_THIS]); - names[GET_MEMBER] = new Name(Lazy.NF_constructorMethod, names[DMH_THIS]); + names[NEW_OBJ] = new Name(NF_allocateInstance, names[DMH_THIS]); + names[GET_MEMBER] = new Name(NF_constructorMethod, names[DMH_THIS]); } else if (needsInit) { - names[GET_MEMBER] = new Name(Lazy.NF_internalMemberNameEnsureInit, names[DMH_THIS]); + names[GET_MEMBER] = new Name(NF_internalMemberNameEnsureInit, names[DMH_THIS]); } else { - names[GET_MEMBER] = new Name(Lazy.NF_internalMemberName, names[DMH_THIS]); + names[GET_MEMBER] = new Name(NF_internalMemberName, names[DMH_THIS]); } assert(findDirectMethodHandle(names[GET_MEMBER]) == names[DMH_THIS]); Object[] outArgs = Arrays.copyOfRange(names, ARG_BASE, GET_MEMBER+1, Object[].class); @@ -250,9 +250,9 @@ } static Object findDirectMethodHandle(Name name) { - if (name.function == Lazy.NF_internalMemberName || - name.function == Lazy.NF_internalMemberNameEnsureInit || - name.function == Lazy.NF_constructorMethod) { + if (name.function == NF_internalMemberName || + name.function == NF_internalMemberNameEnsureInit || + name.function == NF_constructorMethod) { assert(name.arguments.length == 1); return name.arguments[0]; } @@ -613,18 +613,18 @@ final int RESULT = nameCursor-1; // either the call or the cast Name[] names = arguments(nameCursor - ARG_LIMIT, mtype.invokerType()); if (needsInit) - names[INIT_BAR] = new Name(Lazy.NF_ensureInitialized, names[DMH_THIS]); + names[INIT_BAR] = new Name(NF_ensureInitialized, names[DMH_THIS]); if (needsCast && !isGetter) - names[PRE_CAST] = new Name(Lazy.NF_checkCast, names[DMH_THIS], names[SET_VALUE]); + names[PRE_CAST] = new Name(NF_checkCast, names[DMH_THIS], names[SET_VALUE]); Object[] outArgs = new Object[1 + linkerType.parameterCount()]; assert(outArgs.length == (isGetter ? 3 : 4)); outArgs[0] = UNSAFE; if (isStatic) { - outArgs[1] = names[F_HOLDER] = new Name(Lazy.NF_staticBase, names[DMH_THIS]); - outArgs[2] = names[F_OFFSET] = new Name(Lazy.NF_staticOffset, names[DMH_THIS]); + outArgs[1] = names[F_HOLDER] = new Name(NF_staticBase, names[DMH_THIS]); + outArgs[2] = names[F_OFFSET] = new Name(NF_staticOffset, names[DMH_THIS]); } else { - outArgs[1] = names[OBJ_CHECK] = new Name(Lazy.NF_checkBase, names[OBJ_BASE]); - outArgs[2] = names[F_OFFSET] = new Name(Lazy.NF_fieldOffset, names[DMH_THIS]); + outArgs[1] = names[OBJ_CHECK] = new Name(NF_checkBase, names[OBJ_BASE]); + outArgs[2] = names[F_OFFSET] = new Name(NF_fieldOffset, names[DMH_THIS]); } if (!isGetter) { outArgs[3] = (needsCast ? names[PRE_CAST] : names[SET_VALUE]); @@ -632,7 +632,7 @@ for (Object a : outArgs) assert(a != null); names[LINKER_CALL] = new Name(linker, outArgs); if (needsCast && isGetter) - names[POST_CAST] = new Name(Lazy.NF_checkCast, names[DMH_THIS], names[LINKER_CALL]); + names[POST_CAST] = new Name(NF_checkCast, names[DMH_THIS], names[LINKER_CALL]); for (Name n : names) assert(n != null); String fieldOrStatic = (isStatic ? "Static" : "Field"); String lambdaName = (linkerName + fieldOrStatic); // significant only for debugging @@ -645,50 +645,45 @@ * Pre-initialized NamedFunctions for bootstrapping purposes. * Factored in an inner class to delay initialization until first usage. */ - private static class Lazy { - static final NamedFunction - NF_internalMemberName, - NF_internalMemberNameEnsureInit, - NF_ensureInitialized, - NF_fieldOffset, - NF_checkBase, - NF_staticBase, - NF_staticOffset, - NF_checkCast, - NF_allocateInstance, - NF_constructorMethod; - static { - try { - NamedFunction nfs[] = { - NF_internalMemberName = new NamedFunction(DirectMethodHandle.class - .getDeclaredMethod("internalMemberName", Object.class)), - NF_internalMemberNameEnsureInit = new NamedFunction(DirectMethodHandle.class - .getDeclaredMethod("internalMemberNameEnsureInit", Object.class)), - NF_ensureInitialized = new NamedFunction(DirectMethodHandle.class - .getDeclaredMethod("ensureInitialized", Object.class)), - NF_fieldOffset = new NamedFunction(DirectMethodHandle.class - .getDeclaredMethod("fieldOffset", Object.class)), - NF_checkBase = new NamedFunction(DirectMethodHandle.class - .getDeclaredMethod("checkBase", Object.class)), - NF_staticBase = new NamedFunction(DirectMethodHandle.class - .getDeclaredMethod("staticBase", Object.class)), - NF_staticOffset = new NamedFunction(DirectMethodHandle.class - .getDeclaredMethod("staticOffset", Object.class)), - NF_checkCast = new NamedFunction(DirectMethodHandle.class - .getDeclaredMethod("checkCast", Object.class, Object.class)), - NF_allocateInstance = new NamedFunction(DirectMethodHandle.class - .getDeclaredMethod("allocateInstance", Object.class)), - NF_constructorMethod = new NamedFunction(DirectMethodHandle.class - .getDeclaredMethod("constructorMethod", 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); - } + static final NamedFunction + NF_internalMemberName, + NF_internalMemberNameEnsureInit, + NF_ensureInitialized, + NF_fieldOffset, + NF_checkBase, + NF_staticBase, + NF_staticOffset, + NF_checkCast, + NF_allocateInstance, + NF_constructorMethod; + static { + try { + NamedFunction nfs[] = { + NF_internalMemberName = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("internalMemberName", Object.class)), + NF_internalMemberNameEnsureInit = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("internalMemberNameEnsureInit", Object.class)), + NF_ensureInitialized = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("ensureInitialized", Object.class)), + NF_fieldOffset = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("fieldOffset", Object.class)), + NF_checkBase = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("checkBase", Object.class)), + NF_staticBase = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("staticBase", Object.class)), + NF_staticOffset = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("staticOffset", Object.class)), + NF_checkCast = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("checkCast", Object.class, Object.class)), + NF_allocateInstance = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("allocateInstance", Object.class)), + NF_constructorMethod = new NamedFunction(DirectMethodHandle.class + .getDeclaredMethod("constructorMethod", Object.class)) + }; + // Each nf must be statically invocable or we get tied up in our bootstraps. + assert(InvokerBytecodeGenerator.isStaticallyInvocable(nfs)); + } catch (ReflectiveOperationException ex) { + throw newInternalError(ex); } } } diff --git a/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java b/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java --- a/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java +++ b/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java @@ -750,7 +750,7 @@ assert(!isLinkerMethodInvoke(name)); // should use the static path for these if (true) { // push receiver - MethodHandle target = name.function.resolvedHandle; + MethodHandle target = name.function.resolvedHandle(); assert(target != null) : name.exprString(); mv.visitLdcInsn(constantPlaceholder(target)); emitReferenceCast(MethodHandle.class, target); @@ -779,6 +779,15 @@ //MethodHandle.class already covered }; + static boolean isStaticallyInvocable(NamedFunction[] functions) { + for (NamedFunction nf : functions) { + if (!isStaticallyInvocable(nf.member)) { + return false; + } + } + return true; + } + static boolean isStaticallyInvocable(Name name) { return isStaticallyInvocable(name.function.member()); } @@ -881,7 +890,7 @@ // The array will be a constant. Object emptyArray; try { - emptyArray = name.function.resolvedHandle.invoke(); + emptyArray = name.function.resolvedHandle().invoke(); } catch (Throwable ex) { throw newInternalError(ex); } @@ -1085,8 +1094,8 @@ Label L_handler = new Label(); Label L_done = new Label(); - Class returnType = result.function.resolvedHandle.type().returnType(); - MethodType type = args.function.resolvedHandle.type() + Class returnType = result.function.resolvedHandle().type().returnType(); + MethodType type = args.function.resolvedHandle().type() .dropParameterTypes(0,1) .changeReturnType(returnType); diff --git a/src/java.base/share/classes/java/lang/invoke/Invokers.java b/src/java.base/share/classes/java/lang/invoke/Invokers.java --- a/src/java.base/share/classes/java/lang/invoke/Invokers.java +++ b/src/java.base/share/classes/java/lang/invoke/Invokers.java @@ -429,11 +429,8 @@ NF_checkCustomized = new NamedFunction(Invokers.class .getDeclaredMethod("checkCustomized", 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(); - } + // Each nf must be statically invocable or we get tied up in our bootstraps. + assert(InvokerBytecodeGenerator.isStaticallyInvocable(nfs)); } catch (ReflectiveOperationException ex) { throw newInternalError(ex); } diff --git a/src/java.base/share/classes/java/lang/invoke/LambdaForm.java b/src/java.base/share/classes/java/lang/invoke/LambdaForm.java --- a/src/java.base/share/classes/java/lang/invoke/LambdaForm.java +++ b/src/java.base/share/classes/java/lang/invoke/LambdaForm.java @@ -1024,7 +1024,7 @@ static class NamedFunction { final MemberName member; - @Stable MethodHandle resolvedHandle; + private @Stable MethodHandle resolvedHandle; @Stable MethodHandle invoker; NamedFunction(MethodHandle resolvedHandle) { @@ -1074,8 +1074,10 @@ return resolvedHandle; } - void resolve() { - resolvedHandle = DirectMethodHandle.make(member); + synchronized void resolve() { + if (resolvedHandle == null) { + resolvedHandle = DirectMethodHandle.make(member); + } } @Override @@ -1235,6 +1237,7 @@ traceInterpreter("| getInvoker", this); invoker(); } + // resolvedHandle might be uninitialized, ok for tracing if (resolvedHandle == null) { traceInterpreter("| resolve", this); resolvedHandle(); diff --git a/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java b/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java --- a/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java +++ b/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java @@ -541,7 +541,7 @@ assert(pos > 0); // cannot spread the MH arg itself Name spreadParam = new Name(L_TYPE); - Name checkSpread = new Name(MethodHandleImpl.Lazy.NF_checkSpreadArgument, spreadParam, arrayLength); + Name checkSpread = new Name(MethodHandleImpl.NF_checkSpreadArgument, spreadParam, arrayLength); // insert the new expressions int exprPos = lambdaForm.arity(); diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java b/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java --- a/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java @@ -219,7 +219,7 @@ if (convSpec == null) continue; MethodHandle fn; if (convSpec instanceof Class) { - fn = Lazy.MH_cast.bindTo(convSpec); + fn = getConstantHandle(MH_cast).bindTo(convSpec); } else { fn = (MethodHandle) convSpec; } @@ -239,7 +239,7 @@ if (convSpec == void.class) fn = null; else - fn = Lazy.MH_cast.bindTo(convSpec); + fn = getConstantHandle(MH_cast).bindTo(convSpec); } else { fn = (MethodHandle) convSpec; } @@ -302,7 +302,7 @@ Name conv; if (convSpec instanceof Class) { Class convClass = (Class) convSpec; - conv = new Name(Lazy.MH_cast, convClass, names[INARG_BASE + i]); + conv = new Name(getConstantHandle(MH_cast), convClass, names[INARG_BASE + i]); } else { MethodHandle fn = (MethodHandle) convSpec; conv = new Name(fn, names[INARG_BASE + i]); @@ -326,7 +326,7 @@ conv = new Name(LambdaForm.constantZero(BasicType.basicType(srcType.returnType()))); } else if (convSpec instanceof Class) { Class convClass = (Class) convSpec; - conv = new Name(Lazy.MH_cast, convClass, names[OUT_CALL]); + conv = new Name(getConstantHandle(MH_cast), convClass, names[OUT_CALL]); } else { MethodHandle fn = (MethodHandle) convSpec; if (fn.type().parameterCount() == 0) @@ -529,7 +529,7 @@ // Spread the array. MethodHandle aload = MethodHandles.arrayElementGetter(spreadArgType); Name array = names[argIndex]; - names[nameCursor++] = new Name(Lazy.NF_checkSpreadArgument, array, spreadArgCount); + names[nameCursor++] = new Name(NF_checkSpreadArgument, array, spreadArgCount); for (int j = 0; j < spreadArgCount; i++, j++) { indexes[i] = nameCursor; names[nameCursor++] = new Name(aload, array, j); @@ -566,66 +566,6 @@ throw newIllegalArgumentException("array is not of length "+n); } - /** - * Pre-initialized NamedFunctions for bootstrapping purposes. - * Factored in an inner class to delay initialization until first usage. - */ - static class Lazy { - private static final Class MHI = MethodHandleImpl.class; - private static final Class CLS = Class.class; - - private static final MethodHandle[] ARRAYS; - private static final MethodHandle[] FILL_ARRAYS; - - static final NamedFunction NF_checkSpreadArgument; - static final NamedFunction NF_guardWithCatch; - static final NamedFunction NF_throwException; - static final NamedFunction NF_profileBoolean; - - static final MethodHandle MH_cast; - static final MethodHandle MH_selectAlternative; - static final MethodHandle MH_copyAsPrimitiveArray; - static final MethodHandle MH_fillNewTypedArray; - static final MethodHandle MH_fillNewArray; - static final MethodHandle MH_arrayIdentity; - - static { - ARRAYS = makeArrays(); - FILL_ARRAYS = makeFillArrays(); - - try { - NF_checkSpreadArgument = new NamedFunction(MHI.getDeclaredMethod("checkSpreadArgument", Object.class, int.class)); - NF_guardWithCatch = new NamedFunction(MHI.getDeclaredMethod("guardWithCatch", MethodHandle.class, Class.class, - MethodHandle.class, Object[].class)); - NF_throwException = new NamedFunction(MHI.getDeclaredMethod("throwException", Throwable.class)); - NF_profileBoolean = new NamedFunction(MHI.getDeclaredMethod("profileBoolean", boolean.class, int[].class)); - - NF_checkSpreadArgument.resolve(); - NF_guardWithCatch.resolve(); - NF_throwException.resolve(); - NF_profileBoolean.resolve(); - - MH_cast = IMPL_LOOKUP.findVirtual(CLS, "cast", - MethodType.methodType(Object.class, Object.class)); - MH_copyAsPrimitiveArray = IMPL_LOOKUP.findStatic(MHI, "copyAsPrimitiveArray", - MethodType.methodType(Object.class, Wrapper.class, Object[].class)); - MH_arrayIdentity = IMPL_LOOKUP.findStatic(MHI, "identity", - MethodType.methodType(Object[].class, Object[].class)); - MH_fillNewArray = IMPL_LOOKUP.findStatic(MHI, "fillNewArray", - MethodType.methodType(Object[].class, Integer.class, Object[].class)); - MH_fillNewTypedArray = IMPL_LOOKUP.findStatic(MHI, "fillNewTypedArray", - MethodType.methodType(Object[].class, Object[].class, Integer.class, Object[].class)); - - MH_selectAlternative = makeIntrinsic( - IMPL_LOOKUP.findStatic(MHI, "selectAlternative", - MethodType.methodType(MethodHandle.class, boolean.class, MethodHandle.class, MethodHandle.class)), - Intrinsic.SELECT_ALTERNATIVE); - } catch (ReflectiveOperationException ex) { - throw newInternalError(ex); - } - } - } - /** Factory method: Collect or filter selected argument(s). */ static MethodHandle makeCollectArguments(MethodHandle target, MethodHandle collector, int collectArgPos, boolean retainOriginalArgs) { @@ -911,10 +851,10 @@ // profile branch if (PROFILE != -1) { - names[PROFILE] = new Name(Lazy.NF_profileBoolean, names[CALL_TEST], names[GET_COUNTERS]); + names[PROFILE] = new Name(NF_profileBoolean, names[CALL_TEST], names[GET_COUNTERS]); } // call selectAlternative - names[SELECT_ALT] = new Name(Lazy.MH_selectAlternative, names[TEST], names[GET_TARGET], names[GET_FALLBACK]); + names[SELECT_ALT] = new Name(getConstantHandle(MH_selectAlternative), names[TEST], names[GET_TARGET], names[GET_FALLBACK]); // call target or fallback invokeArgs[0] = names[SELECT_ALT]; @@ -989,7 +929,7 @@ // t_{i+1}:L=MethodHandleImpl.guardWithCatch(target:L,exType:L,catcher:L,t_{i}:L); Object[] gwcArgs = new Object[] {names[GET_TARGET], names[GET_CLASS], names[GET_CATCHER], names[BOXED_ARGS]}; - names[TRY_CATCH] = new Name(Lazy.NF_guardWithCatch, gwcArgs); + names[TRY_CATCH] = new Name(NF_guardWithCatch, gwcArgs); // t_{i+2}:I=MethodHandle.invokeBasic(unbox:L,t_{i+1}:L); MethodHandle invokeBasicUnbox = MethodHandles.basicInvoker(MethodType.methodType(basicType.rtype(), Object.class)); @@ -1073,7 +1013,7 @@ mh = MethodHandles.dropArguments(mh, 1, type.parameterList().subList(1, arity)); return mh; } - return makePairwiseConvert(Lazy.NF_throwException.resolvedHandle(), type, false, true); + return makePairwiseConvert(NF_throwException.resolvedHandle(), type, false, true); } static Empty throwException(T t) throws T { throw t; } @@ -1421,25 +1361,7 @@ { return makeArray(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); } private static final int ARRAYS_COUNT = 11; - - private static MethodHandle[] makeArrays() { - MethodHandle[] mhs = new MethodHandle[MAX_ARITY + 1]; - for (int i = 0; i < ARRAYS_COUNT; i++) { - MethodHandle mh = findCollector("array", i, Object[].class); - mh = makeIntrinsic(mh, Intrinsic.NEW_ARRAY); - mhs[i] = mh; - } - assert(assertArrayMethodCount(mhs)); - return mhs; - } - - private static boolean assertArrayMethodCount(MethodHandle[] mhs) { - assert(findCollector("array", ARRAYS_COUNT, Object[].class) == null); - for (int i = 0; i < ARRAYS_COUNT; i++) { - assert(mhs[i] != null); - } - return true; - } + private static final @Stable MethodHandle[] ARRAYS = new MethodHandle[MAX_ARITY + 1]; // filling versions of the above: // using Integer len instead of int len and no varargs to avoid bootstrapping problems @@ -1488,24 +1410,17 @@ { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); return a; } private static final int FILL_ARRAYS_COUNT = 11; // current number of fillArray methods + private static final @Stable MethodHandle[] FILL_ARRAYS = new MethodHandle[FILL_ARRAYS_COUNT]; - private static MethodHandle[] makeFillArrays() { - MethodHandle[] mhs = new MethodHandle[FILL_ARRAYS_COUNT]; - mhs[0] = null; // there is no empty fill; at least a0 is required - for (int i = 1; i < FILL_ARRAYS_COUNT; i++) { - MethodHandle mh = findCollector("fillArray", i, Object[].class, Integer.class, Object[].class); - mhs[i] = mh; + private static MethodHandle getFillArray(int count) { + assert (count > 0 && count < FILL_ARRAYS_COUNT); + MethodHandle mh = FILL_ARRAYS[count]; + if (mh != null) { + return mh; } - assert(assertFillArrayMethodCount(mhs)); - return mhs; - } - - private static boolean assertFillArrayMethodCount(MethodHandle[] mhs) { - assert(findCollector("fillArray", FILL_ARRAYS_COUNT, Object[].class, Integer.class, Object[].class) == null); - for (int i = 1; i < FILL_ARRAYS_COUNT; i++) { - assert(mhs[i] != null); - } - return true; + mh = findCollector("fillArray", count, Object[].class, Integer.class, Object[].class); + FILL_ARRAYS[count] = mh; + return mh; } private static Object copyAsPrimitiveArray(Wrapper w, Object... boxes) { @@ -1518,12 +1433,19 @@ * arguments and returns an Object array of them, as if for varargs. */ static MethodHandle varargsArray(int nargs) { - MethodHandle mh = Lazy.ARRAYS[nargs]; - if (mh != null) return mh; - mh = buildVarargsArray(Lazy.MH_fillNewArray, Lazy.MH_arrayIdentity, nargs); + MethodHandle mh = ARRAYS[nargs]; + if (mh != null) { + return mh; + } + if (nargs < ARRAYS_COUNT) { + mh = findCollector("array", nargs, Object[].class); + } else { + mh = buildVarargsArray(getConstantHandle(MH_fillNewArray), + getConstantHandle(MH_arrayIdentity), nargs); + } assert(assertCorrectArity(mh, nargs)); mh = makeIntrinsic(mh, Intrinsic.NEW_ARRAY); - return Lazy.ARRAYS[nargs] = mh; + return ARRAYS[nargs] = mh; } private static boolean assertCorrectArity(MethodHandle mh, int arity) { @@ -1531,7 +1453,7 @@ return true; } - // Array identity function (used as Lazy.MH_arrayIdentity). + // Array identity function (used as getConstantHandle(MH_arrayIdentity)). static T[] identity(T[] x) { return x; } @@ -1547,12 +1469,12 @@ MethodHandle mh = finisher; if (rightLen > 0) { MethodHandle rightFiller = fillToRight(LEFT_ARGS + rightLen); - if (mh == Lazy.MH_arrayIdentity) + if (mh.equals(getConstantHandle(MH_arrayIdentity))) mh = rightFiller; else mh = MethodHandles.collectArguments(mh, 0, rightFiller); } - if (mh == Lazy.MH_arrayIdentity) + if (mh.equals(getConstantHandle(MH_arrayIdentity))) mh = leftCollector; else mh = MethodHandles.collectArguments(mh, 0, leftCollector); @@ -1560,7 +1482,7 @@ } private static final int LEFT_ARGS = FILL_ARRAYS_COUNT - 1; - private static final MethodHandle[] FILL_ARRAY_TO_RIGHT = new MethodHandle[MAX_ARITY+1]; + private static final @Stable MethodHandle[] FILL_ARRAY_TO_RIGHT = new MethodHandle[MAX_ARITY+1]; /** fill_array_to_right(N).invoke(a, argL..arg[N-1]) * fills a[L]..a[N-1] with corresponding arguments, * and then returns a. The value L is a global constant (LEFT_ARGS). @@ -1574,7 +1496,7 @@ } private static MethodHandle buildFiller(int nargs) { if (nargs <= LEFT_ARGS) - return Lazy.MH_arrayIdentity; // no args to fill; return the array unchanged + return getConstantHandle(MH_arrayIdentity); // no args to fill; return the array unchanged // we need room for both mh and a in mh.invoke(a, arg*[nargs]) final int CHUNK = LEFT_ARGS; int rightLen = nargs % CHUNK; @@ -1590,7 +1512,7 @@ if (midLen < LEFT_ARGS) rightLen = nargs - (midLen = LEFT_ARGS); assert(rightLen > 0); MethodHandle midFill = fillToRight(midLen); // recursive fill - MethodHandle rightFill = Lazy.FILL_ARRAYS[rightLen].bindTo(midLen); // [midLen..nargs-1] + MethodHandle rightFill = getFillArray(rightLen).bindTo(midLen); // [midLen..nargs-1] assert(midFill.type().parameterCount() == 1 + midLen - LEFT_ARGS); assert(rightFill.type().parameterCount() == 1 + rightLen); @@ -1641,14 +1563,14 @@ 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 builder = getConstantHandle(MH_fillNewArray); MethodHandle producer = buildArrayProducer(arrayType); mh = buildVarargsArray(builder, producer, nargs); } else { 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; // must be weakly typed + MethodHandle builder = getConstantHandle(MH_fillNewTypedArray).bindTo(example); + MethodHandle producer = getConstantHandle(MH_arrayIdentity); // must be weakly typed mh = buildVarargsArray(builder, producer, nargs); } mh = mh.asType(MethodType.methodType(arrayType, Collections.>nCopies(nargs, elemType))); @@ -1662,7 +1584,7 @@ private static MethodHandle buildArrayProducer(Class arrayType) { Class elemType = arrayType.getComponentType(); assert(elemType.isPrimitive()); - return Lazy.MH_copyAsPrimitiveArray.bindTo(Wrapper.forPrimitiveType(elemType)); + return getConstantHandle(MH_copyAsPrimitiveArray).bindTo(Wrapper.forPrimitiveType(elemType)); } /*non-public*/ static void assertSame(Object mh1, Object mh2) { @@ -1673,4 +1595,91 @@ throw newInternalError(msg); } } + + // Local constant functions: + /*non-public*/ static final NamedFunction + NF_checkSpreadArgument, + NF_guardWithCatch, + NF_throwException, + NF_profileBoolean; + + static { + try { + NamedFunction nfs[] = { + NF_checkSpreadArgument = new NamedFunction(MethodHandleImpl.class + .getDeclaredMethod("checkSpreadArgument", Object.class, int.class)), + NF_guardWithCatch = new NamedFunction(MethodHandleImpl.class + .getDeclaredMethod("guardWithCatch", MethodHandle.class, Class.class, + MethodHandle.class, Object[].class)), + NF_throwException = new NamedFunction(MethodHandleImpl.class + .getDeclaredMethod("throwException", Throwable.class)), + NF_profileBoolean = new NamedFunction(MethodHandleImpl.class + .getDeclaredMethod("profileBoolean", boolean.class, int[].class)) + }; + // Each nf must be statically invocable or we get tied up in our bootstraps. + assert(InvokerBytecodeGenerator.isStaticallyInvocable(nfs)); + } catch (ReflectiveOperationException ex) { + throw newInternalError(ex); + } + } + + // Indexes into constant method handles: + private static final int + MH_cast = 0, + MH_selectAlternative = 1, + MH_copyAsPrimitiveArray = 2, + MH_fillNewTypedArray = 3, + MH_fillNewArray = 4, + MH_arrayIdentity = 5, + MH_LIMIT = 6; + + private static MethodHandle getConstantHandle(int idx) { + MethodHandle handle = HANDLES[idx]; + if (handle != null) { + return handle; + } + return setCachedHandle(idx, makeConstantHandle(idx)); + } + + private static synchronized MethodHandle setCachedHandle(int idx, final MethodHandle method) { + // Simulate a CAS, to avoid racy duplication of results. + MethodHandle prev = HANDLES[idx]; + if (prev != null) { + return prev; + } + HANDLES[idx] = method; + return method; + } + + // Local constant method handles: + private static final @Stable MethodHandle[] HANDLES = new MethodHandle[MH_LIMIT]; + + private static MethodHandle makeConstantHandle(int idx) { + try { + switch (idx) { + case MH_cast: + return IMPL_LOOKUP.findVirtual(Class.class, "cast", + MethodType.methodType(Object.class, Object.class)); + case MH_copyAsPrimitiveArray: + return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "copyAsPrimitiveArray", + MethodType.methodType(Object.class, Wrapper.class, Object[].class)); + case MH_arrayIdentity: + return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "identity", + MethodType.methodType(Object[].class, Object[].class)); + case MH_fillNewArray: + return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "fillNewArray", + MethodType.methodType(Object[].class, Integer.class, Object[].class)); + case MH_fillNewTypedArray: + return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "fillNewTypedArray", + MethodType.methodType(Object[].class, Object[].class, Integer.class, Object[].class)); + case MH_selectAlternative: + return makeIntrinsic(IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "selectAlternative", + MethodType.methodType(MethodHandle.class, boolean.class, MethodHandle.class, MethodHandle.class)), + Intrinsic.SELECT_ALTERNATIVE); + } + } catch (ReflectiveOperationException ex) { + throw newInternalError(ex); + } + throw newInternalError("Unknown function index: " + idx); + } }