From ee089afcf8e95ddc96ca2b1f2c3a8d1ee7567deb Mon Sep 17 00:00:00 2001 From: jrose Date: Sat, 5 Oct 2013 05:30:38 -0700 Subject: [PATCH] 8024761: JSR 292 improve performance of generic invocation Summary: use a per-MH one element cache for MH.asType to speed up MH.invoke; also cache enough MH constants to cache LMF.metafactory Reviewed-by: twisti --- .../java/lang/invoke/BoundMethodHandle.java | 5 + .../classes/java/lang/invoke/CallSite.java | 48 +++- .../java/lang/invoke/InvokeGeneric.java | 148 ----------- .../classes/java/lang/invoke/Invokers.java | 233 +++++++++--------- .../classes/java/lang/invoke/LambdaForm.java | 31 ++- .../classes/java/lang/invoke/MemberName.java | 75 ++++-- .../java/lang/invoke/MethodHandle.java | 22 +- .../java/lang/invoke/MethodHandleImpl.java | 23 +- .../java/lang/invoke/MethodHandleNatives.java | 42 ++-- .../java/lang/invoke/MethodHandles.java | 93 +++++-- .../java/lang/invoke/MethodTypeForm.java | 19 +- 11 files changed, 392 insertions(+), 347 deletions(-) delete mode 100644 src/share/classes/java/lang/invoke/InvokeGeneric.java diff --git a/src/share/classes/java/lang/invoke/BoundMethodHandle.java b/src/share/classes/java/lang/invoke/BoundMethodHandle.java index 846920a59..9aaafd7c7 100644 --- a/src/share/classes/java/lang/invoke/BoundMethodHandle.java +++ b/src/share/classes/java/lang/invoke/BoundMethodHandle.java @@ -360,6 +360,10 @@ import jdk.internal.org.objectweb.asm.Type; return new Name(mh, mhName); } + NamedFunction getterFunction(int i) { + return new NamedFunction(getters[i]); + } + static final SpeciesData EMPTY = new SpeciesData("", BoundMethodHandle.class); private SpeciesData(String types, Class clazz) { @@ -394,6 +398,7 @@ import jdk.internal.org.objectweb.asm.Type; private boolean isPlaceholder() { return clazz == null; } private static final HashMap CACHE = new HashMap<>(); + static { CACHE.put("", EMPTY); } // make bootstrap predictable private static final boolean INIT_DONE; // set after finishes... SpeciesData extendWithType(char type) { diff --git a/src/share/classes/java/lang/invoke/CallSite.java b/src/share/classes/java/lang/invoke/CallSite.java index d83464155..e08f1db47 100644 --- a/src/share/classes/java/lang/invoke/CallSite.java +++ b/src/share/classes/java/lang/invoke/CallSite.java @@ -261,7 +261,7 @@ public class CallSite { Object info, // Caller information: Class callerClass) { - Object caller = IMPL_LOOKUP.in(callerClass); + MethodHandles.Lookup caller = IMPL_LOOKUP.in(callerClass); CallSite site; try { Object binding; @@ -273,14 +273,44 @@ public class CallSite { } else { Object[] argv = (Object[]) info; maybeReBoxElements(argv); - if (3 + argv.length > 255) - throw new BootstrapMethodError("too many bootstrap method arguments"); - MethodType bsmType = bootstrapMethod.type(); - if (bsmType.parameterCount() == 4 && bsmType.parameterType(3) == Object[].class) - binding = bootstrapMethod.invoke(caller, name, type, argv); - else - binding = MethodHandles.spreadInvoker(bsmType, 3) - .invoke(bootstrapMethod, caller, name, type, argv); + switch (argv.length) { + case 0: + binding = bootstrapMethod.invoke(caller, name, type); + break; + case 1: + binding = bootstrapMethod.invoke(caller, name, type, + argv[0]); + break; + case 2: + binding = bootstrapMethod.invoke(caller, name, type, + argv[0], argv[1]); + break; + case 3: + binding = bootstrapMethod.invoke(caller, name, type, + argv[0], argv[1], argv[2]); + break; + case 4: + binding = bootstrapMethod.invoke(caller, name, type, + argv[0], argv[1], argv[2], argv[3]); + break; + case 5: + binding = bootstrapMethod.invoke(caller, name, type, + argv[0], argv[1], argv[2], argv[3], argv[4]); + break; + case 6: + binding = bootstrapMethod.invoke(caller, name, type, + argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); + break; + default: + final int NON_SPREAD_ARG_COUNT = 3; // (caller, name, type) + if (NON_SPREAD_ARG_COUNT + argv.length > MethodType.MAX_MH_ARITY) + throw new BootstrapMethodError("too many bootstrap method arguments"); + MethodType bsmType = bootstrapMethod.type(); + MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argv.length); + MethodHandle typedBSM = bootstrapMethod.asType(invocationType); + MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT); + binding = spreader.invokeExact(typedBSM, (Object)caller, (Object)name, (Object)type, argv); + } } //System.out.println("BSM for "+name+type+" => "+binding); if (binding instanceof CallSite) { diff --git a/src/share/classes/java/lang/invoke/InvokeGeneric.java b/src/share/classes/java/lang/invoke/InvokeGeneric.java deleted file mode 100644 index 81f663bec..000000000 --- a/src/share/classes/java/lang/invoke/InvokeGeneric.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package java.lang.invoke; - -import sun.invoke.util.*; -import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; - -/** - * Adapters which manage inexact MethodHandle.invoke calls. - * The JVM calls one of these when the exact type match fails. - * @author jrose - */ -class InvokeGeneric { - // erased type for the call, which originates from an inexact invoke site - private final MethodType erasedCallerType; - // an invoker of type (MT, MH; A...) -> R - private final MethodHandle initialInvoker; - - /** Compute and cache information for this adapter, so that it can - * call out to targets of the erasure-family of the given erased type. - */ - /*non-public*/ InvokeGeneric(MethodType erasedCallerType) throws ReflectiveOperationException { - assert(erasedCallerType.equals(erasedCallerType.erase())); - this.erasedCallerType = erasedCallerType; - this.initialInvoker = makeInitialInvoker(); - assert initialInvoker.type().equals(erasedCallerType - .insertParameterTypes(0, MethodType.class, MethodHandle.class)) - : initialInvoker.type(); - } - - private static MethodHandles.Lookup lookup() { - return IMPL_LOOKUP; - } - - /** Return the adapter information for this type's erasure. */ - /*non-public*/ static MethodHandle generalInvokerOf(MethodType erasedCallerType) throws ReflectiveOperationException { - InvokeGeneric gen = new InvokeGeneric(erasedCallerType); - return gen.initialInvoker; - } - - private MethodHandle makeInitialInvoker() throws ReflectiveOperationException { - // postDispatch = #(MH'; MT, MH; A...){MH'(MT, MH; A)} - MethodHandle postDispatch = makePostDispatchInvoker(); - MethodHandle invoker; - if (returnConversionPossible()) { - invoker = MethodHandles.foldArguments(postDispatch, - dispatcher("dispatchWithConversion")); - } else { - invoker = MethodHandles.foldArguments(postDispatch, dispatcher("dispatch")); - } - return invoker; - } - - private static final Class[] EXTRA_ARGS = { MethodType.class, MethodHandle.class }; - private MethodHandle makePostDispatchInvoker() { - // Take (MH'; MT, MH; A...) and run MH'(MT, MH; A...). - MethodType invokerType = erasedCallerType.insertParameterTypes(0, EXTRA_ARGS); - return invokerType.invokers().exactInvoker(); - } - private MethodHandle dropDispatchArguments(MethodHandle targetInvoker) { - assert(targetInvoker.type().parameterType(0) == MethodHandle.class); - return MethodHandles.dropArguments(targetInvoker, 1, EXTRA_ARGS); - } - - private MethodHandle dispatcher(String dispatchName) throws ReflectiveOperationException { - return lookup().bind(this, dispatchName, - MethodType.methodType(MethodHandle.class, - MethodType.class, MethodHandle.class)); - } - - static final boolean USE_AS_TYPE_PATH = true; - - /** Return a method handle to invoke on the callerType, target, and remaining arguments. - * The method handle must finish the call. - * This is the first look at the caller type and target. - */ - private MethodHandle dispatch(MethodType callerType, MethodHandle target) { - MethodType targetType = target.type(); - if (USE_AS_TYPE_PATH || target.isVarargsCollector()) { - MethodHandle newTarget = target.asType(callerType); - targetType = callerType; - Invokers invokers = targetType.invokers(); - MethodHandle invoker = invokers.erasedInvokerWithDrops; - if (invoker == null) { - invokers.erasedInvokerWithDrops = invoker = - dropDispatchArguments(invokers.erasedInvoker()); - } - return invoker.bindTo(newTarget); - } - throw new RuntimeException("NYI"); - } - - private MethodHandle dispatchWithConversion(MethodType callerType, MethodHandle target) { - MethodHandle finisher = dispatch(callerType, target); - if (returnConversionNeeded(callerType, target)) - finisher = addReturnConversion(finisher, callerType.returnType()); //FIXME: slow - return finisher; - } - - private boolean returnConversionPossible() { - Class needType = erasedCallerType.returnType(); - return !needType.isPrimitive(); - } - private boolean returnConversionNeeded(MethodType callerType, MethodHandle target) { - Class needType = callerType.returnType(); - if (needType == erasedCallerType.returnType()) - return false; // no conversions possible, since must be primitive or Object - Class haveType = target.type().returnType(); - if (VerifyType.isNullConversion(haveType, needType) && !needType.isInterface()) - return false; - return true; - } - private MethodHandle addReturnConversion(MethodHandle finisher, Class type) { - // FIXME: This is slow because it creates a closure node on every call that requires a return cast. - MethodType finisherType = finisher.type(); - MethodHandle caster = ValueConversions.identity(type); - caster = caster.asType(caster.type().changeParameterType(0, finisherType.returnType())); - finisher = MethodHandles.filterReturnValue(finisher, caster); - return finisher.asType(finisherType); - } - - public String toString() { - return "InvokeGeneric"+erasedCallerType; - } -} diff --git a/src/share/classes/java/lang/invoke/Invokers.java b/src/share/classes/java/lang/invoke/Invokers.java index 0ef6078b6..6214aad86 100644 --- a/src/share/classes/java/lang/invoke/Invokers.java +++ b/src/share/classes/java/lang/invoke/Invokers.java @@ -44,6 +44,7 @@ class Invokers { // exact invoker for the outgoing call private /*lazy*/ MethodHandle exactInvoker; + private /*lazy*/ MethodHandle basicInvoker; // invokeBasic (unchecked exact) // erased (partially untyped but with primitives) invoker for the outgoing call // FIXME: get rid of @@ -74,21 +75,7 @@ class Invokers { /*non-public*/ MethodHandle exactInvoker() { MethodHandle invoker = exactInvoker; if (invoker != null) return invoker; - MethodType mtype = targetType; - MethodType invokerType = mtype.invokerType(); - LambdaForm lform; - final int MTYPE_ARG_APPENDED = 1; // argument count for appended mtype value - if (mtype.parameterSlotCount() <= MethodType.MAX_MH_INVOKER_ARITY - MTYPE_ARG_APPENDED) { - lform = invokeForm(mtype, false, MethodTypeForm.LF_EX_INVOKER); - invoker = BoundMethodHandle.bindSingle(invokerType, lform, mtype); - } else { - // At maximum arity, we cannot afford an extra mtype argument, - // so build a fully customized (non-cached) invoker form. - lform = invokeForm(mtype, true, MethodTypeForm.LF_EX_INVOKER); - invoker = SimpleMethodHandle.make(invokerType, lform); - } - invoker = invoker.withInternalMemberName(MemberName.makeMethodHandleInvoke("invokeExact", mtype)); - assert(checkInvoker(invoker)); + invoker = makeExactOrGeneralInvoker(true); exactInvoker = invoker; return invoker; } @@ -96,43 +83,56 @@ class Invokers { /*non-public*/ MethodHandle generalInvoker() { MethodHandle invoker = generalInvoker; if (invoker != null) return invoker; + invoker = makeExactOrGeneralInvoker(false); + generalInvoker = invoker; + return invoker; + } + + private MethodHandle makeExactOrGeneralInvoker(boolean isExact) { MethodType mtype = targetType; MethodType invokerType = mtype.invokerType(); - LambdaForm lform; - final int MTYPE_ARG_APPENDED = 1; // argument count for appended mtype value - assert(GENERIC_INVOKER_SLOP >= MTYPE_ARG_APPENDED); - if (mtype.parameterSlotCount() <= MethodType.MAX_MH_INVOKER_ARITY - GENERIC_INVOKER_SLOP) { - prepareForGenericCall(mtype); - lform = invokeForm(mtype, false, MethodTypeForm.LF_GEN_INVOKER); - invoker = BoundMethodHandle.bindSingle(invokerType, lform, mtype); - } else { - // At maximum arity, we cannot afford an extra mtype argument, - // so build a fully customized (non-cached) invoker form. - lform = invokeForm(mtype, true, MethodTypeForm.LF_GEN_INVOKER); - invoker = SimpleMethodHandle.make(invokerType, lform); - } - invoker = invoker.withInternalMemberName(MemberName.makeMethodHandleInvoke("invoke", mtype)); + int which = (isExact ? MethodTypeForm.LF_EX_INVOKER : MethodTypeForm.LF_GEN_INVOKER); + LambdaForm lform = invokeHandleForm(mtype, false, which); + MethodHandle invoker = BoundMethodHandle.bindSingle(invokerType, lform, mtype); + String whichName = (isExact ? "invokeExact" : "invoke"); + invoker = invoker.withInternalMemberName(MemberName.makeMethodHandleInvoke(whichName, mtype)); assert(checkInvoker(invoker)); - generalInvoker = invoker; + maybeCompileToBytecode(invoker); return invoker; } - /*non-public*/ MethodHandle makeBasicInvoker() { - MethodHandle invoker = DirectMethodHandle.make(invokeBasicMethod(targetType)); - assert(targetType == targetType.basicType()); - // Note: This is not cached here. It is cached by the calling MethodTypeForm. + /** If the target type seems to be common enough, eagerly compile the invoker to bytecodes. */ + private void maybeCompileToBytecode(MethodHandle invoker) { + final int EAGER_COMPILE_ARITY_LIMIT = 10; + if (targetType == targetType.erase() && + targetType.parameterCount() < EAGER_COMPILE_ARITY_LIMIT) { + invoker.form.compileToBytecode(); + } + } + + /*non-public*/ MethodHandle basicInvoker() { + MethodHandle invoker = basicInvoker; + if (invoker != null) return invoker; + MethodType basicType = targetType.basicType(); + if (basicType != targetType) { + // double cache; not used significantly + return basicInvoker = basicType.invokers().basicInvoker(); + } + MemberName method = invokeBasicMethod(basicType); + invoker = DirectMethodHandle.make(method); assert(checkInvoker(invoker)); + basicInvoker = invoker; return invoker; } - static MemberName invokeBasicMethod(MethodType type) { - type = type.basicType(); - String name = "invokeBasic"; + // This next one is called from LambdaForm.NamedFunction.. + /*non-public*/ static MemberName invokeBasicMethod(MethodType basicType) { + assert(basicType == basicType.basicType()); try { //Lookup.findVirtual(MethodHandle.class, name, type); - return IMPL_LOOKUP.resolveOrFail(REF_invokeVirtual, MethodHandle.class, name, type); + return IMPL_LOOKUP.resolveOrFail(REF_invokeVirtual, MethodHandle.class, "invokeBasic", basicType); } catch (ReflectiveOperationException ex) { - throw newInternalError("JVM cannot find invoker for "+type, ex); + throw newInternalError("JVM cannot find invoker for "+basicType, ex); } } @@ -184,6 +184,7 @@ class Invokers { vaInvoker = MethodHandles.filterArgument(arrayInvoker, 0, makeSpreader); } assert(vaInvoker.type().equals(spreadInvokerType.invokerType())); + maybeCompileToBytecode(vaInvoker); spreadInvokers[leadingArgCount] = vaInvoker; return vaInvoker; } @@ -231,32 +232,38 @@ class Invokers { return "Invokers"+targetType; } - static MemberName exactInvokerMethod(MethodType mtype, Object[] appendixResult) { - LambdaForm lform; - final int MTYPE_ARG_APPENDED = 1; // argument count for appended mtype value - if (mtype.parameterSlotCount() <= MethodType.MAX_MH_ARITY - MTYPE_ARG_APPENDED) { - lform = invokeForm(mtype, false, MethodTypeForm.LF_EX_LINKER); - appendixResult[0] = mtype; - } else { - lform = invokeForm(mtype, true, MethodTypeForm.LF_EX_LINKER); + static MemberName methodHandleInvokeLinkerMethod(String name, + MethodType mtype, + Object[] appendixResult) { + int which; + switch (name) { + case "invokeExact": which = MethodTypeForm.LF_EX_LINKER; break; + case "invoke": which = MethodTypeForm.LF_GEN_LINKER; break; + default: throw new InternalError("not invoker: "+name); } - return lform.vmentry; - } - - static MemberName genericInvokerMethod(MethodType mtype, Object[] appendixResult) { LambdaForm lform; - final int MTYPE_ARG_APPENDED = 1; // argument count for appended mtype value - if (mtype.parameterSlotCount() <= MethodType.MAX_MH_ARITY - (MTYPE_ARG_APPENDED + GENERIC_INVOKER_SLOP)) { - lform = invokeForm(mtype, false, MethodTypeForm.LF_GEN_LINKER); + if (mtype.parameterSlotCount() <= MethodType.MAX_MH_ARITY - MH_LINKER_ARG_APPENDED) { + lform = invokeHandleForm(mtype, false, which); appendixResult[0] = mtype; - prepareForGenericCall(mtype); } else { - lform = invokeForm(mtype, true, MethodTypeForm.LF_GEN_LINKER); + lform = invokeHandleForm(mtype, true, which); } return lform.vmentry; } - private static LambdaForm invokeForm(MethodType mtype, boolean customized, int which) { + // argument count to account for trailing "appendix value" (typically the mtype) + private static final int MH_LINKER_ARG_APPENDED = 1; + + /** Returns an adapter for invokeExact or generic invoke, as a MH or constant pool linker. + * If !customized, caller is responsible for supplying, during adapter execution, + * a copy of the exact mtype. This is because the adapter might be generalized to + * a basic type. + * @param mtype the caller's method type (either basic or full-custom) + * @param customized whether to use a trailing appendix argument (to carry the mtype) + * @param which bit-encoded 0x01 whether it is a CP adapter ("linker") or MHs.invoker value ("invoker"); + * 0x02 whether it is for invokeExact or generic invoke + */ + private static LambdaForm invokeHandleForm(MethodType mtype, boolean customized, int which) { boolean isCached; if (!customized) { mtype = mtype.basicType(); // normalize Z to I, String to Object, etc. @@ -301,41 +308,24 @@ class Invokers { : Arrays.asList(mtype, customized, which, nameCursor, names.length); if (MTYPE_ARG >= INARG_LIMIT) { assert(names[MTYPE_ARG] == null); - names[MTYPE_ARG] = BoundMethodHandle.getSpeciesData("L").getterName(names[THIS_MH], 0); + NamedFunction getter = BoundMethodHandle.getSpeciesData("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) } // Make the final call. If isGeneric, then prepend the result of type checking. - MethodType outCallType; - Object[] outArgs; + MethodType outCallType = mtype.basicType(); + Object[] outArgs = Arrays.copyOfRange(names, CALL_MH, OUTARG_LIMIT, Object[].class); Object mtypeArg = (customized ? mtype : names[MTYPE_ARG]); if (!isGeneric) { names[CHECK_TYPE] = new Name(NF_checkExactType, names[CALL_MH], mtypeArg); // mh.invokeExact(a*):R => checkExactType(mh, TYPEOF(a*:R)); mh.invokeBasic(a*) - outArgs = Arrays.copyOfRange(names, CALL_MH, OUTARG_LIMIT, Object[].class); - outCallType = mtype; - } else if (customized) { - names[CHECK_TYPE] = new Name(NF_asType, names[CALL_MH], mtypeArg); - // mh.invokeGeneric(a*):R => - // let mt=TYPEOF(a*:R), tmh=asType(mh, mt); - // tmh.invokeBasic(a*) - outArgs = Arrays.copyOfRange(names, CALL_MH, OUTARG_LIMIT, Object[].class); - outCallType = mtype; } else { names[CHECK_TYPE] = new Name(NF_checkGenericType, names[CALL_MH], mtypeArg); - // mh.invokeGeneric(a*):R => - // let mt=TYPEOF(a*:R), gamh=checkGenericType(mh, mt); - // gamh.invokeBasic(mt, mh, a*) - final int PREPEND_GAMH = 0, PREPEND_MT = 1, PREPEND_COUNT = 2; - assert(GENERIC_INVOKER_SLOP == PREPEND_COUNT); - outArgs = Arrays.copyOfRange(names, CALL_MH, OUTARG_LIMIT + PREPEND_COUNT, Object[].class); - // prepend arguments: - System.arraycopy(outArgs, 0, outArgs, PREPEND_COUNT, outArgs.length - PREPEND_COUNT); - outArgs[PREPEND_GAMH] = names[CHECK_TYPE]; - outArgs[PREPEND_MT] = mtypeArg; - outCallType = mtype.insertParameterTypes(0, MethodType.class, MethodHandle.class); + // mh.invokeGeneric(a*):R => checkGenericType(mh, TYPEOF(a*:R)).invokeBasic(a*) + outArgs[0] = names[CHECK_TYPE]; } - names[LINKER_CALL] = new Name(invokeBasicMethod(outCallType), outArgs); + names[LINKER_CALL] = new Name(outCallType, outArgs); lform = new LambdaForm(debugName, INARG_LIMIT, names); if (isLinker) lform.compileToBytecode(); // JVM needs a real methodOop @@ -343,7 +333,6 @@ class Invokers { lform = mtype.form().setCachedLambdaForm(which, lform); return lform; } - private static final int GENERIC_INVOKER_SLOP = 2; // used elsewhere to avoid arity problems /*non-public*/ static WrongMethodTypeException newWrongMethodTypeException(MethodType actual, MethodType expected) { @@ -362,47 +351,53 @@ class Invokers { throw newWrongMethodTypeException(expected, actual); } - /** Static definition of MethodHandle.invokeGeneric checking code. */ + /** Static definition of MethodHandle.invokeGeneric checking code. + * Directly returns the type-adjusted MH to invoke, as follows: + * {@code (R)MH.invoke(a*) => MH.asType(TYPEOF(a*:R)).invokeBasic(a*)} + */ /*non-public*/ static @ForceInline Object checkGenericType(Object mhObj, Object expectedObj) { MethodHandle mh = (MethodHandle) mhObj; MethodType expected = (MethodType) expectedObj; - //MethodType actual = mh.type(); - MethodHandle gamh = expected.form().genericInvoker; - if (gamh != null) return gamh; - return prepareForGenericCall(expected); + 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*), + * let MT0 = TYPEOF(a*:R), MT1 = MH.type + * + * if MT0==MT1 or MT1 can be safely called by MT0 + * => MH.invokeBasic(a*) + * if MT1 can be safely called by MT0[R := Object] + * => MH.invokeBasic(a*) & checkcast(R) + * if MT1 can be safely called by MT0[* := Object] + * => checkcast(A)* & MH.invokeBasic(a*) & checkcast(R) + * if a big adapter BA can be pulled out of (MT0,MT1) + * => BA.invokeBasic(MT0,MH,a*) + * if a local adapter LA can cached on static CS0 = new GICS(MT0) + * => CS0.LA.invokeBasic(MH,a*) + * else + * => MH.asType(MT0).invokeBasic(A*) + */ } - /** - * Returns an adapter GA for invoking a MH with type adjustments. - * The MethodType of the generic invocation site is prepended to MH - * and its arguments as follows: - * {@code (R)MH.invoke(A*) => GA.invokeBasic(TYPEOF, MH, A*)} - */ - /*non-public*/ static MethodHandle prepareForGenericCall(MethodType mtype) { - // force any needed adapters to be preconstructed - MethodTypeForm form = mtype.form(); - MethodHandle gamh = form.genericInvoker; - if (gamh != null) return gamh; - try { - // Trigger adapter creation. - gamh = InvokeGeneric.generalInvokerOf(form.erasedType); - form.genericInvoker = gamh; - return gamh; - } catch (Exception ex) { - throw newInternalError("Exception while resolving inexact invoke", ex); - } + static MemberName linkToCallSiteMethod(MethodType mtype) { + LambdaForm lform = callSiteForm(mtype, false); + return lform.vmentry; } - static MemberName linkToCallSiteMethod(MethodType mtype) { - LambdaForm lform = callSiteForm(mtype); + static MemberName linkToTargetMethod(MethodType mtype) { + LambdaForm lform = callSiteForm(mtype, true); return lform.vmentry; } - private static LambdaForm callSiteForm(MethodType mtype) { + // skipCallSite is true if we are optimizing a ConstantCallSite + private static LambdaForm callSiteForm(MethodType mtype, boolean skipCallSite) { mtype = mtype.basicType(); // normalize Z to I, String to Object, etc. - LambdaForm lform = mtype.form().cachedLambdaForm(MethodTypeForm.LF_CS_LINKER); + final int which = (skipCallSite ? MethodTypeForm.LF_MH_LINKER : MethodTypeForm.LF_CS_LINKER); + LambdaForm lform = mtype.form().cachedLambdaForm(which); if (lform != null) return lform; // exactInvokerForm (Object,Object)Object // link with java.lang.invoke.MethodHandle.invokeBasic(MethodHandle,Object,Object)Object/invokeSpecial @@ -410,24 +405,26 @@ class Invokers { final int OUTARG_LIMIT = ARG_BASE + mtype.parameterCount(); final int INARG_LIMIT = OUTARG_LIMIT + 1; int nameCursor = OUTARG_LIMIT; - final int CSITE_ARG = nameCursor++; // the last in-argument - final int CALL_MH = nameCursor++; // result of getTarget + final int APPENDIX_ARG = nameCursor++; // the last in-argument + final int CSITE_ARG = skipCallSite ? -1 : APPENDIX_ARG; + final int CALL_MH = skipCallSite ? APPENDIX_ARG : nameCursor++; // result of getTarget final int LINKER_CALL = nameCursor++; - MethodType invokerFormType = mtype.appendParameterTypes(CallSite.class); + MethodType invokerFormType = mtype.appendParameterTypes(skipCallSite ? MethodHandle.class : CallSite.class); Name[] names = arguments(nameCursor - INARG_LIMIT, invokerFormType); assert(names.length == nameCursor); - assert(names[CSITE_ARG] != null); - names[CALL_MH] = new Name(NF_getCallSiteTarget, names[CSITE_ARG]); + assert(names[APPENDIX_ARG] != null); + if (!skipCallSite) + names[CALL_MH] = new Name(NF_getCallSiteTarget, names[CSITE_ARG]); // (site.)invokedynamic(a*):R => mh = site.getTarget(); mh.invokeBasic(a*) final int PREPEND_MH = 0, PREPEND_COUNT = 1; Object[] outArgs = Arrays.copyOfRange(names, ARG_BASE, OUTARG_LIMIT + PREPEND_COUNT, Object[].class); // prepend MH argument: System.arraycopy(outArgs, 0, outArgs, PREPEND_COUNT, outArgs.length - PREPEND_COUNT); outArgs[PREPEND_MH] = names[CALL_MH]; - names[LINKER_CALL] = new Name(invokeBasicMethod(mtype), outArgs); - lform = new LambdaForm("linkToCallSite", INARG_LIMIT, names); + names[LINKER_CALL] = new Name(mtype, outArgs); + lform = new LambdaForm((skipCallSite ? "linkToTargetMethod" : "linkToCallSite"), INARG_LIMIT, names); lform.compileToBytecode(); // JVM needs a real methodOop - lform = mtype.form().setCachedLambdaForm(MethodTypeForm.LF_CS_LINKER, lform); + lform = mtype.form().setCachedLambdaForm(which, lform); return lform; } diff --git a/src/share/classes/java/lang/invoke/LambdaForm.java b/src/share/classes/java/lang/invoke/LambdaForm.java index 96d83fb47..d83fc4780 100644 --- a/src/share/classes/java/lang/invoke/LambdaForm.java +++ b/src/share/classes/java/lang/invoke/LambdaForm.java @@ -457,7 +457,7 @@ class LambdaForm { isCompiled = true; return vmentry; } catch (Error | Exception ex) { - throw newInternalError(this.toString(), ex); + throw newInternalError("compileToBytecode", ex); } } @@ -683,8 +683,9 @@ class LambdaForm { */ static void traceInterpreter(String event, Object obj, Object... args) { - if (!TRACE_INTERPRETER) return; - System.out.println("LFI: "+event+" "+(obj != null ? obj : "")+(args != null && args.length != 0 ? Arrays.asList(args) : "")); + if (TRACE_INTERPRETER) { + System.out.println("LFI: "+event+" "+(obj != null ? obj : "")+(args != null && args.length != 0 ? Arrays.asList(args) : "")); + } } static void traceInterpreter(String event, Object obj) { traceInterpreter(event, obj, (Object[])null); @@ -982,6 +983,16 @@ class LambdaForm { //resolvedHandle = eraseSubwordTypes(resolvedHandle); this.resolvedHandle = resolvedHandle; } + NamedFunction(MethodType basicInvokerType) { + assert(basicInvokerType == basicInvokerType.basicType()) : basicInvokerType; + if (basicInvokerType.parameterSlotCount() < MethodType.MAX_MH_INVOKER_ARITY) { + this.resolvedHandle = basicInvokerType.invokers().basicInvoker(); + this.member = resolvedHandle.internalMemberName(); + } else { + // necessary to pass BigArityTest + this.member = Invokers.invokeBasicMethod(basicInvokerType); + } + } // The next 3 constructors are used to break circular dependencies on MH.invokeStatic, etc. // Any LambdaForm containing such a member is not interpretable. @@ -1229,7 +1240,7 @@ class LambdaForm { } public String toString() { - if (member == null) return resolvedHandle.toString(); + if (member == null) return String.valueOf(resolvedHandle); return member.getDeclaringClass().getSimpleName()+"."+member.getName(); } } @@ -1279,6 +1290,10 @@ class LambdaForm { Name(MethodHandle function, Object... arguments) { this(new NamedFunction(function), arguments); } + Name(MethodType functionType, Object... arguments) { + this(new NamedFunction(functionType), arguments); + assert(arguments[0] instanceof Name && ((Name)arguments[0]).type == 'L'); + } Name(MemberName function, Object... arguments) { this(new NamedFunction(function), arguments); } @@ -1622,4 +1637,12 @@ class LambdaForm { */ static { NamedFunction.initializeInvokers(); } + + // The following hack is necessary in order to suppress TRACE_INTERPRETER + // during execution of the static initializes of this class. + // Turning on TRACE_INTERPRETER too early will cause + // stack overflows and other misbehavior during attempts to trace events + // that occur during LambdaForm.. + // Therefore, do not move this line higher in this file, and do not remove. + private static final boolean TRACE_INTERPRETER = MethodHandleStatics.TRACE_INTERPRETER; } diff --git a/src/share/classes/java/lang/invoke/MemberName.java b/src/share/classes/java/lang/invoke/MemberName.java index 05d291c25..5b6cecba9 100644 --- a/src/share/classes/java/lang/invoke/MemberName.java +++ b/src/share/classes/java/lang/invoke/MemberName.java @@ -556,6 +556,9 @@ import java.util.Objects; } throw new IllegalArgumentException(this.toString()); } + /** If this MN is not REF_newInvokeSpecial, return a clone with that ref. kind. + * In that case it must already be REF_invokeSpecial. + */ public MemberName asConstructor() { switch (getReferenceKind()) { case REF_invokeSpecial: return clone().changeReferenceKind(REF_newInvokeSpecial, REF_invokeSpecial); @@ -563,6 +566,32 @@ import java.util.Objects; } throw new IllegalArgumentException(this.toString()); } + /** If this MN is a REF_invokeSpecial, return a clone with the "normal" kind + * REF_invokeVirtual; also switch either to REF_invokeInterface if clazz.isInterface. + * The end result is to get a fully virtualized version of the MN. + * (Note that resolving in the JVM will sometimes devirtualize, changing + * REF_invokeVirtual of a final to REF_invokeSpecial, and REF_invokeInterface + * in some corner cases to either of the previous two; this transform + * undoes that change under the assumption that it occurred.) + */ + public MemberName asNormalOriginal() { + byte normalVirtual = clazz.isInterface() ? REF_invokeInterface : REF_invokeVirtual; + byte refKind = getReferenceKind(); + byte newRefKind = refKind; + MemberName result = this; + switch (refKind) { + case REF_invokeInterface: + case REF_invokeVirtual: + case REF_invokeSpecial: + newRefKind = normalVirtual; + break; + } + if (newRefKind == refKind) + return this; + result = clone().changeReferenceKind(newRefKind, refKind); + assert(this.referenceKindIsConsistentWith(result.getReferenceKind())); + return result; + } /** Create a name for the given reflected constructor. The resulting name will be in a resolved state. */ @SuppressWarnings("LeakingThisInConstructor") public MemberName(Constructor ctor) { @@ -660,7 +689,7 @@ import java.util.Objects; @Override public int hashCode() { - return Objects.hash(clazz, flags, name, getType()); + return Objects.hash(clazz, getReferenceKind(), name, getType()); } @Override public boolean equals(Object that) { @@ -676,13 +705,14 @@ import java.util.Objects; if (this == that) return true; if (that == null) return false; return this.clazz == that.clazz - && this.flags == that.flags + && this.getReferenceKind() == that.getReferenceKind() && Objects.equals(this.name, that.name) && Objects.equals(this.getType(), that.getType()); } // Construction from symbolic parts, for queries: - /** Create a field or type name from the given components: Declaring class, name, type, reference kind. + /** Create a field or type name from the given components: + * Declaring class, name, type, reference kind. * The declaring class may be supplied as null if this is to be a bare name and type. * The resulting name will in an unresolved state. */ @@ -706,21 +736,34 @@ import java.util.Objects; * The resulting name will in an unresolved state. */ public MemberName(Class defClass, String name, MethodType type, byte refKind) { - @SuppressWarnings("LocalVariableHidesMemberVariable") - int flags = (name != null && name.equals(CONSTRUCTOR_NAME) ? IS_CONSTRUCTOR : IS_METHOD); - init(defClass, name, type, flagsMods(flags, 0, refKind)); + int initFlags = (name != null && name.equals(CONSTRUCTOR_NAME) ? IS_CONSTRUCTOR : IS_METHOD); + init(defClass, name, type, flagsMods(initFlags, 0, refKind)); + initResolved(false); + } + /** Create a method, constructor, or field name from the given components: + * Reference kind, declaring class, name, type. + */ + public MemberName(byte refKind, Class defClass, String name, Object type) { + int kindFlags; + if (MethodHandleNatives.refKindIsField(refKind)) { + kindFlags = IS_FIELD; + if (!(type instanceof Class)) + throw newIllegalArgumentException("not a field type"); + } else if (MethodHandleNatives.refKindIsMethod(refKind)) { + kindFlags = IS_METHOD; + if (!(type instanceof MethodType)) + throw newIllegalArgumentException("not a method type"); + } else if (refKind == REF_newInvokeSpecial) { + kindFlags = IS_CONSTRUCTOR; + if (!(type instanceof MethodType) || + !CONSTRUCTOR_NAME.equals(name)) + throw newIllegalArgumentException("not a constructor type or name"); + } else { + throw newIllegalArgumentException("bad reference kind "+refKind); + } + init(defClass, name, type, flagsMods(kindFlags, 0, refKind)); initResolved(false); } -// /** Create a method or constructor name from the given components: Declaring class, name, type, modifiers. -// * 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 modifier flags default to zero. -// * The resulting name will in an unresolved state. -// */ -// public MemberName(Class defClass, String name, MethodType type, Void unused) { -// this(defClass, name, type, REF_NONE); -// } - /** Query whether this member name is resolved to a non-static, non-final method. */ public boolean hasReceiverTypeDispatch() { diff --git a/src/share/classes/java/lang/invoke/MethodHandle.java b/src/share/classes/java/lang/invoke/MethodHandle.java index 613d223fe..25bb5e585 100644 --- a/src/share/classes/java/lang/invoke/MethodHandle.java +++ b/src/share/classes/java/lang/invoke/MethodHandle.java @@ -420,6 +420,8 @@ public abstract class MethodHandle { private final MethodType type; /*private*/ final LambdaForm form; // form is not private so that invokers can easily fetch it + /*private*/ MethodHandle asTypeCache; + // asTypeCache is not private so that invokers can easily fetch it /** * Reports the type of this method handle. @@ -739,10 +741,24 @@ public abstract class MethodHandle { * @see MethodHandles#explicitCastArguments */ public MethodHandle asType(MethodType newType) { - if (!type.isConvertibleTo(newType)) { - throw new WrongMethodTypeException("cannot convert "+this+" to "+newType); + // Fast path alternative to a heavyweight {@code asType} call. + // Return 'this' if the conversion will be a no-op. + if (newType == type) { + return this; + } + // Return 'this.asTypeCache' if the conversion is already memoized. + MethodHandle atc = asTypeCache; + if (atc != null && newType == atc.type) { + return atc; } - return convertArguments(newType); + return asTypeUncached(newType); + } + + /** Override this to change asType behavior. */ + /*non-public*/ MethodHandle asTypeUncached(MethodType newType) { + if (!type.isConvertibleTo(newType)) + throw new WrongMethodTypeException("cannot convert "+this+" to "+newType); + return asTypeCache = convertArguments(newType); } /** diff --git a/src/share/classes/java/lang/invoke/MethodHandleImpl.java b/src/share/classes/java/lang/invoke/MethodHandleImpl.java index 6918fec1c..f5fc1b7a7 100644 --- a/src/share/classes/java/lang/invoke/MethodHandleImpl.java +++ b/src/share/classes/java/lang/invoke/MethodHandleImpl.java @@ -314,13 +314,13 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; static class AsVarargsCollector extends MethodHandle { private final MethodHandle target; private final Class arrayType; - private MethodHandle cache; + private /*@Stable*/ MethodHandle asCollectorCache; AsVarargsCollector(MethodHandle target, MethodType type, Class arrayType) { super(type, reinvokerForm(target)); this.target = target; this.arrayType = arrayType; - this.cache = target.asCollector(arrayType, 0); + this.asCollectorCache = target.asCollector(arrayType, 0); } @Override MethodHandle reinvokerTarget() { return target; } @@ -336,18 +336,19 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; } @Override - public MethodHandle asType(MethodType newType) { + public MethodHandle asTypeUncached(MethodType newType) { MethodType type = this.type(); int collectArg = type.parameterCount() - 1; int newArity = newType.parameterCount(); if (newArity == collectArg+1 && type.parameterType(collectArg).isAssignableFrom(newType.parameterType(collectArg))) { // if arity and trailing parameter are compatible, do normal thing - return asFixedArity().asType(newType); + return asTypeCache = asFixedArity().asType(newType); } // check cache - if (cache.type().parameterCount() == newArity) - return cache.asType(newType); + MethodHandle acc = asCollectorCache; + if (acc != null && acc.type().parameterCount() == newArity) + return asTypeCache = acc.asType(newType); // build and cache a collector int arrayLength = newArity - collectArg; MethodHandle collector; @@ -357,8 +358,8 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; } catch (IllegalArgumentException ex) { throw new WrongMethodTypeException("cannot build collector", ex); } - cache = collector; - return collector.asType(newType); + asCollectorCache = collector; + return asTypeCache = collector.asType(newType); } @Override @@ -977,6 +978,12 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; return target; } @Override + public MethodHandle asTypeUncached(MethodType newType) { + // This MH is an alias for target, except for the MemberName + // Drop the MemberName if there is any conversion. + return asTypeCache = target.asType(newType); + } + @Override MemberName internalMemberName() { return member; } diff --git a/src/share/classes/java/lang/invoke/MethodHandleNatives.java b/src/share/classes/java/lang/invoke/MethodHandleNatives.java index 9a71299a0..56cb245e6 100644 --- a/src/share/classes/java/lang/invoke/MethodHandleNatives.java +++ b/src/share/classes/java/lang/invoke/MethodHandleNatives.java @@ -233,20 +233,19 @@ class MethodHandleNatives { } static String refKindName(byte refKind) { assert(refKindIsValid(refKind)); - return REFERENCE_KIND_NAME[refKind]; + switch (refKind) { + case REF_getField: return "getField"; + case REF_getStatic: return "getStatic"; + case REF_putField: return "putField"; + case REF_putStatic: return "putStatic"; + case REF_invokeVirtual: return "invokeVirtual"; + case REF_invokeStatic: return "invokeStatic"; + case REF_invokeSpecial: return "invokeSpecial"; + case REF_newInvokeSpecial: return "newInvokeSpecial"; + case REF_invokeInterface: return "invokeInterface"; + default: return "REF_???"; + } } - private static String[] REFERENCE_KIND_NAME = { - null, - "getField", - "getStatic", - "putField", - "putStatic", - "invokeVirtual", - "invokeStatic", - "invokeSpecial", - "newInvokeSpecial", - "invokeInterface" - }; private static native int getNamedCon(int which, Object[] name); static boolean verifyConstants() { @@ -294,12 +293,18 @@ class MethodHandleNatives { Class caller = (Class)callerObj; String name = nameObj.toString().intern(); MethodType type = (MethodType)typeObj; - appendixResult[0] = CallSite.makeSite(bootstrapMethod, + CallSite callSite = CallSite.makeSite(bootstrapMethod, name, type, staticArguments, caller); - return Invokers.linkToCallSiteMethod(type); + if (callSite instanceof ConstantCallSite) { + appendixResult[0] = callSite.dynamicInvoker(); + return Invokers.linkToTargetMethod(type); + } else { + appendixResult[0] = callSite; + return Invokers.linkToCallSiteMethod(type); + } } /** @@ -388,12 +393,7 @@ class MethodHandleNatives { Object[] appendixResult) { try { if (defc == MethodHandle.class && refKind == REF_invokeVirtual) { - switch (name) { - case "invoke": - return Invokers.genericInvokerMethod(fixMethodType(callerClass, type), appendixResult); - case "invokeExact": - return Invokers.exactInvokerMethod(fixMethodType(callerClass, type), appendixResult); - } + return Invokers.methodHandleInvokeLinkerMethod(name, fixMethodType(callerClass, type), appendixResult); } } catch (Throwable ex) { if (ex instanceof LinkageError) diff --git a/src/share/classes/java/lang/invoke/MethodHandles.java b/src/share/classes/java/lang/invoke/MethodHandles.java index f0f9447e0..a4f217f5a 100644 --- a/src/share/classes/java/lang/invoke/MethodHandles.java +++ b/src/share/classes/java/lang/invoke/MethodHandles.java @@ -39,6 +39,7 @@ import sun.reflect.misc.ReflectUtil; import sun.security.util.SecurityConstants; import static java.lang.invoke.MethodHandleStatics.*; import static java.lang.invoke.MethodHandleNatives.Constants.*; +import java.util.concurrent.ConcurrentHashMap; import sun.security.util.SecurityConstants; /** @@ -1090,19 +1091,30 @@ return mh1; MemberName resolveOrFail(byte refKind, Class refc, String name, Class type) throws NoSuchFieldException, IllegalAccessException { checkSymbolicClass(refc); // do this before attempting to resolve - name.getClass(); type.getClass(); // NPE + name.getClass(); // NPE + type.getClass(); // NPE return IMPL_NAMES.resolveOrFail(refKind, new MemberName(refc, name, type, refKind), lookupClassOrNull(), NoSuchFieldException.class); } MemberName resolveOrFail(byte refKind, Class refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException { checkSymbolicClass(refc); // do this before attempting to resolve - name.getClass(); type.getClass(); // NPE + name.getClass(); // NPE + type.getClass(); // NPE return IMPL_NAMES.resolveOrFail(refKind, new MemberName(refc, name, type, refKind), lookupClassOrNull(), NoSuchMethodException.class); } + MemberName resolveOrFail(byte refKind, MemberName member) throws ReflectiveOperationException { + checkSymbolicClass(member.getDeclaringClass()); // do this before attempting to resolve + member.getName().getClass(); // NPE + member.getType().getClass(); // NPE + return IMPL_NAMES.resolveOrFail(refKind, member, lookupClassOrNull(), + ReflectiveOperationException.class); + } + void checkSymbolicClass(Class refc) throws IllegalAccessException { + refc.getClass(); // NPE Class caller = lookupClassOrNull(); if (caller != null && !VerifyAccess.isClassAccessible(refc, caller, allowedModes)) throw new MemberName(refc).makeAccessException("symbolic reference class is not public", this); @@ -1348,29 +1360,74 @@ return mh1; */ /*non-public*/ MethodHandle linkMethodHandleConstant(byte refKind, Class defc, String name, Object type) throws ReflectiveOperationException { - MemberName resolved = null; - if (type instanceof MemberName) { - resolved = (MemberName) type; - if (!resolved.isResolved()) throw new InternalError("unresolved MemberName"); - assert(name == null || name.equals(resolved.getName())); + if (!(type instanceof Class || type instanceof MethodType)) + throw new InternalError("unresolved MemberName"); + MemberName member = new MemberName(refKind, defc, name, type); + MethodHandle mh = LOOKASIDE_TABLE.get(member); + if (mh != null) { + checkSymbolicClass(defc); + return mh; + } + MemberName resolved = resolveOrFail(refKind, member); + mh = getDirectMethodHandle(refKind, defc, resolved); + if (mh instanceof DirectMethodHandle + && canBeCached(refKind, defc, resolved)) { + MemberName key = mh.internalMemberName(); + if (key != null) { + key = key.asNormalOriginal(); + } + if (member.equals(key)) { // better safe than sorry + LOOKASIDE_TABLE.put(key, (DirectMethodHandle) mh); + } } + return mh; + } + private + boolean canBeCached(byte refKind, Class defc, MemberName member) { + if (refKind == REF_invokeSpecial) { + return false; + } + if (!Modifier.isPublic(defc.getModifiers()) || + !Modifier.isPublic(member.getDeclaringClass().getModifiers()) || + !member.isPublic() || + member.isCallerSensitive()) { + return false; + } + ClassLoader loader = defc.getClassLoader(); + if (!sun.misc.VM.isSystemDomainLoader(loader)) { + ClassLoader sysl = ClassLoader.getSystemClassLoader(); + boolean found = false; + while (sysl != null) { + if (loader == sysl) { found = true; break; } + sysl = sysl.getParent(); + } + if (!found) { + return false; + } + } + try { + MemberName resolved2 = publicLookup().resolveOrFail(refKind, + new MemberName(refKind, defc, member.getName(), member.getType())); + checkSecurityManager(defc, resolved2); + } catch (ReflectiveOperationException | SecurityException ex) { + return false; + } + return true; + } + private + MethodHandle getDirectMethodHandle(byte refKind, Class defc, MemberName member) throws ReflectiveOperationException { if (MethodHandleNatives.refKindIsField(refKind)) { - MemberName field = (resolved != null) ? resolved - : resolveOrFail(refKind, defc, name, (Class) type); - return getDirectField(refKind, defc, field); + return getDirectField(refKind, defc, member); } else if (MethodHandleNatives.refKindIsMethod(refKind)) { - MemberName method = (resolved != null) ? resolved - : resolveOrFail(refKind, defc, name, (MethodType) type); - return getDirectMethod(refKind, defc, method, lookupClass); + return getDirectMethod(refKind, defc, member, lookupClass); } else if (refKind == REF_newInvokeSpecial) { - assert(name == null || name.equals("")); - MemberName ctor = (resolved != null) ? resolved - : resolveOrFail(REF_newInvokeSpecial, defc, name, (MethodType) type); - return getDirectConstructor(defc, ctor); + return getDirectConstructor(defc, member); } // oops - throw new ReflectiveOperationException("bad MethodHandle constant #"+refKind+" "+name+" : "+type); + throw newIllegalArgumentException("bad MethodHandle constant #"+member); } + + static ConcurrentHashMap LOOKASIDE_TABLE = new ConcurrentHashMap<>(); } /** diff --git a/src/share/classes/java/lang/invoke/MethodTypeForm.java b/src/share/classes/java/lang/invoke/MethodTypeForm.java index f279035c4..09a07b187 100644 --- a/src/share/classes/java/lang/invoke/MethodTypeForm.java +++ b/src/share/classes/java/lang/invoke/MethodTypeForm.java @@ -28,6 +28,7 @@ package java.lang.invoke; import sun.invoke.util.Wrapper; import static java.lang.invoke.MethodHandleStatics.*; import static java.lang.invoke.MethodHandleNatives.Constants.*; + import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; /** * Shared information for a group of method types, which differ @@ -74,7 +75,8 @@ final class MethodTypeForm { LF_GEN_LINKER = 11, LF_GEN_INVOKER = 12, LF_CS_LINKER = 13, // linkToCallSite_CS - LF_LIMIT = 14; + LF_MH_LINKER = 14, // linkToCallSite_MH + LF_LIMIT = 15; public MethodType erasedType() { return erasedType; @@ -97,11 +99,24 @@ final class MethodTypeForm { assert(erasedType == basicType) : "erasedType: " + erasedType + " != basicType: " + basicType; // primitives must be flattened also MethodHandle invoker = basicInvoker; if (invoker != null) return invoker; - invoker = basicType.invokers().makeBasicInvoker(); + 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. -- GitLab