# HG changeset patch # Parent 059668226bdf519f96961f0ba34deebb6be71a56 8050057: Improve caching of MethodHandle reinvokers Reviewed-by: vlivanov, ? Contributed-by: john.r.rose@oracle.com diff --git a/src/share/classes/java/lang/invoke/BoundMethodHandle.java b/src/share/classes/java/lang/invoke/BoundMethodHandle.java --- a/src/share/classes/java/lang/invoke/BoundMethodHandle.java +++ b/src/share/classes/java/lang/invoke/BoundMethodHandle.java @@ -117,6 +117,31 @@ return copyWithExtendD(type, form, value); } + @Override + BoundMethodHandle rebind() { + if (!tooComplex()) { + return this; + } + return makeReinvoker(this); + } + + private boolean tooComplex() { + return (fieldCount() > FIELD_COUNT_THRESHOLD || + form.expressionCount() > FORM_EXPRESSION_THRESHOLD); + } + private static final int FIELD_COUNT_THRESHOLD = 12; // largest convenient BMH field count + private static final int FORM_EXPRESSION_THRESHOLD = 24; // largest convenient BMH expression count + + /** + * A reinvoker MH has this form: + * {@code lambda (bmh, arg*) { thismh = bmh[0]; invokeBasic(thismh, arg*) }} + */ + static BoundMethodHandle makeReinvoker(MethodHandle target) { + LambdaForm form = DelegatingMethodHandle.makeReinvokerForm( + target, MethodTypeForm.LF_REBIND, Species_L.SPECIES_DATA.getterFunction(0) ); + return Species_L.make(target.type(), form, target); + } + /** * Return the {@link SpeciesData} instance representing this BMH species. All subclasses must provide a * static field containing this value, and they must accordingly implement this method. @@ -168,15 +193,6 @@ /*non-public*/ abstract BoundMethodHandle copyWithExtendF(MethodType mt, LambdaForm lf, float narg); /*non-public*/ abstract BoundMethodHandle copyWithExtendD(MethodType mt, LambdaForm lf, double narg); - // The following is a grossly irregular hack: - @Override MethodHandle reinvokerTarget() { - try { - return (MethodHandle) arg(0); - } catch (Throwable ex) { - throw newInternalError(ex); - } - } - // // concrete BMH classes required to close bootstrap loops // @@ -188,8 +204,6 @@ super(mt, lf); this.argL0 = argL0; } - // The following is a grossly irregular hack: - @Override MethodHandle reinvokerTarget() { return (MethodHandle) argL0; } @Override /*non-public*/ SpeciesData speciesData() { return SPECIES_DATA; @@ -585,16 +599,6 @@ mv.visitMaxs(0, 0); mv.visitEnd(); - // emit implementation of reinvokerTarget() - mv = cw.visitMethod(NOT_ACC_PUBLIC + ACC_FINAL, "reinvokerTarget", "()" + MH_SIG, null, null); - mv.visitCode(); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, className, "argL0", JLO_SIG); - mv.visitTypeInsn(CHECKCAST, MH); - mv.visitInsn(ARETURN); - mv.visitMaxs(0, 0); - mv.visitEnd(); - // emit implementation of speciesData() mv = cw.visitMethod(NOT_ACC_PUBLIC + ACC_FINAL, "speciesData", MYSPECIES_DATA_SIG, null, null); mv.visitCode(); diff --git a/src/share/classes/java/lang/invoke/DelegatingMethodHandle.java b/src/share/classes/java/lang/invoke/DelegatingMethodHandle.java new file mode 100644 --- /dev/null +++ b/src/share/classes/java/lang/invoke/DelegatingMethodHandle.java @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2014, 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 java.util.Arrays; +import static java.lang.invoke.LambdaForm.*; +import static java.lang.invoke.MethodHandleStatics.*; + +/** + * A method handle whose invocation behavior is determined by a target. + * The delegating MH itself can hold extra "intentions" beyond the simple behavior. + * @author jrose + */ +/*non-public*/ +abstract class DelegatingMethodHandle extends MethodHandle { + protected DelegatingMethodHandle(MethodHandle target) { + this(target.type(), target); + } + + protected DelegatingMethodHandle(MethodType type, MethodHandle target) { + super(type, chooseDelegatingForm(target)); + } + + /** Define this to extract the delegated target which supplies the invocation behavior. */ + abstract protected MethodHandle getTarget(); + + @Override + abstract MethodHandle asTypeUncached(MethodType newType); + + @Override + MemberName internalMemberName() { + return getTarget().internalMemberName(); + } + + @Override + boolean isInvokeSpecial() { + return getTarget().isInvokeSpecial(); + } + + @Override + Class internalCallerClass() { + return getTarget().internalCallerClass(); + } + + @Override + MethodHandle copyWith(MethodType mt, LambdaForm lf) { + // FIXME: rethink 'copyWith' protocol; it is too low-level for use on all MHs + throw newIllegalArgumentException("do not use this"); + } + + @Override + String internalProperties() { + return "\n& Class="+getClass().getSimpleName()+ + "\n& Target="+getTarget().debugString(); + } + + @Override + BoundMethodHandle rebind() { + return getTarget().rebind(); + } + + private static LambdaForm chooseDelegatingForm(MethodHandle target) { + if (target instanceof SimpleMethodHandle) + return target.internalForm(); // no need for an indirection + return makeReinvokerForm(target, MethodTypeForm.LF_DELEGATE, NF_getTarget); + } + + /** Create a LF which simply reinvokes a target of the given basic type. */ + static LambdaForm makeReinvokerForm(MethodHandle target, + int whichCache, + NamedFunction getTargetFn) { + MethodType mtype = target.type().basicType(); + boolean customized = (whichCache < 0 || + mtype.parameterSlotCount() > MethodType.MAX_MH_INVOKER_ARITY); + LambdaForm form; + if (!customized) { + form = mtype.form().cachedLambdaForm(whichCache); + if (form != null) return form; + } + final int THIS_DMH = 0; + final int ARG_BASE = 1; + final int ARG_LIMIT = ARG_BASE + mtype.parameterCount(); + int nameCursor = ARG_LIMIT; + final int NEXT_MH = customized ? -1 : nameCursor++; + final int REINVOKE = nameCursor++; + LambdaForm.Name[] names = LambdaForm.arguments(nameCursor - ARG_LIMIT, mtype.invokerType()); + assert(names.length == nameCursor); + Object[] targetArgs; + if (customized) { + targetArgs = Arrays.copyOfRange(names, ARG_BASE, ARG_LIMIT, Object[].class); + names[REINVOKE] = new LambdaForm.Name(target, targetArgs); // the invoker is the target itself + } else { + names[NEXT_MH] = new LambdaForm.Name(getTargetFn, names[THIS_DMH]); + targetArgs = Arrays.copyOfRange(names, THIS_DMH, ARG_LIMIT, Object[].class); + targetArgs[0] = names[NEXT_MH]; // overwrite this MH with next MH + names[REINVOKE] = new LambdaForm.Name(mtype, targetArgs); + } + String debugString; + switch(whichCache) { + case MethodTypeForm.LF_REBIND: debugString = "BMH.reinvoke"; break; + case MethodTypeForm.LF_DELEGATE: debugString = "MH.delegate"; break; + default: debugString = "MH.reinvoke"; break; + } + form = new LambdaForm(debugString, ARG_LIMIT, names); + if (!customized) { + form = mtype.form().setCachedLambdaForm(whichCache, form); + } + return form; + } + + private static final NamedFunction NF_getTarget; + static { + try { + NF_getTarget = new NamedFunction(DelegatingMethodHandle.class + .getDeclaredMethod("getTarget")); + } catch (ReflectiveOperationException ex) { + throw newInternalError(ex); + } + } +} diff --git a/src/share/classes/java/lang/invoke/DirectMethodHandle.java b/src/share/classes/java/lang/invoke/DirectMethodHandle.java --- a/src/share/classes/java/lang/invoke/DirectMethodHandle.java +++ b/src/share/classes/java/lang/invoke/DirectMethodHandle.java @@ -127,6 +127,11 @@ } @Override + BoundMethodHandle rebind() { + return BoundMethodHandle.makeReinvoker(this); + } + + @Override MethodHandle copyWith(MethodType mt, LambdaForm lf) { assert(this.getClass() == DirectMethodHandle.class); // must override in subclasses return new DirectMethodHandle(mt, lf, member); diff --git a/src/share/classes/java/lang/invoke/LambdaForm.java b/src/share/classes/java/lang/invoke/LambdaForm.java --- a/src/share/classes/java/lang/invoke/LambdaForm.java +++ b/src/share/classes/java/lang/invoke/LambdaForm.java @@ -466,6 +466,11 @@ return arity; } + /** Report the number of expressions (non-parameter names). */ + int expressionCount() { + return names.length - arity; + } + /** Return the method type corresponding to my basic type signature. */ MethodType methodType() { return signatureType(basicTypeSignature()); diff --git a/src/share/classes/java/lang/invoke/MethodHandle.java b/src/share/classes/java/lang/invoke/MethodHandle.java --- a/src/share/classes/java/lang/invoke/MethodHandle.java +++ b/src/share/classes/java/lang/invoke/MethodHandle.java @@ -1385,66 +1385,11 @@ /*non-public*/ abstract MethodHandle copyWith(MethodType mt, LambdaForm lf); - /*non-public*/ - BoundMethodHandle rebind() { - // Bind 'this' into a new invoker, of the known class BMH. - MethodType type2 = type(); - LambdaForm form2 = reinvokerForm(this); - // form2 = lambda (bmh, arg*) { thismh = bmh[0]; invokeBasic(thismh, arg*) } - return BoundMethodHandle.bindSingle(type2, form2, this); - } - - /*non-public*/ - MethodHandle reinvokerTarget() { - throw new InternalError("not a reinvoker MH: "+this.getClass().getName()+": "+this); - } - - /** Create a LF which simply reinvokes a target of the given basic type. - * The target MH must override {@link #reinvokerTarget} to provide the target. + /** Require this method handle to be a BMH, or else replace it with a "wrapper" BMH. + * Many transforms are implemented only for BMHs. + * @return a behaviorally equivalent BMH */ - static LambdaForm reinvokerForm(MethodHandle target) { - MethodType mtype = target.type().basicType(); - LambdaForm reinvoker = mtype.form().cachedLambdaForm(MethodTypeForm.LF_REINVOKE); - if (reinvoker != null) return reinvoker; - if (mtype.parameterSlotCount() >= MethodType.MAX_MH_ARITY) - return makeReinvokerForm(target.type(), target); // cannot cache this - reinvoker = makeReinvokerForm(mtype, null); - return mtype.form().setCachedLambdaForm(MethodTypeForm.LF_REINVOKE, reinvoker); - } - private static LambdaForm makeReinvokerForm(MethodType mtype, MethodHandle customTargetOrNull) { - boolean customized = (customTargetOrNull != null); - MethodHandle MH_invokeBasic = customized ? null : MethodHandles.basicInvoker(mtype); - final int THIS_BMH = 0; - final int ARG_BASE = 1; - final int ARG_LIMIT = ARG_BASE + mtype.parameterCount(); - int nameCursor = ARG_LIMIT; - final int NEXT_MH = customized ? -1 : nameCursor++; - final int REINVOKE = nameCursor++; - LambdaForm.Name[] names = LambdaForm.arguments(nameCursor - ARG_LIMIT, mtype.invokerType()); - Object[] targetArgs; - MethodHandle targetMH; - if (customized) { - targetArgs = Arrays.copyOfRange(names, ARG_BASE, ARG_LIMIT, Object[].class); - targetMH = customTargetOrNull; - } else { - names[NEXT_MH] = new LambdaForm.Name(NF_reinvokerTarget, names[THIS_BMH]); - targetArgs = Arrays.copyOfRange(names, THIS_BMH, ARG_LIMIT, Object[].class); - targetArgs[0] = names[NEXT_MH]; // overwrite this MH with next MH - targetMH = MethodHandles.basicInvoker(mtype); - } - names[REINVOKE] = new LambdaForm.Name(targetMH, targetArgs); - return new LambdaForm("BMH.reinvoke", ARG_LIMIT, names); - } - - private static final LambdaForm.NamedFunction NF_reinvokerTarget; - static { - try { - NF_reinvokerTarget = new LambdaForm.NamedFunction(MethodHandle.class - .getDeclaredMethod("reinvokerTarget")); - } catch (ReflectiveOperationException ex) { - throw newInternalError(ex); - } - } + abstract BoundMethodHandle rebind(); /** * Replace the old lambda form of this method handle with a new one. diff --git a/src/share/classes/java/lang/invoke/MethodHandleImpl.java b/src/share/classes/java/lang/invoke/MethodHandleImpl.java --- a/src/share/classes/java/lang/invoke/MethodHandleImpl.java +++ b/src/share/classes/java/lang/invoke/MethodHandleImpl.java @@ -351,34 +351,46 @@ if (type.parameterType(last) != arrayType) target = target.asType(type.changeParameterType(last, arrayType)); target = target.asFixedArity(); // make sure this attribute is turned off - return new AsVarargsCollector(target, target.type(), arrayType); + return new AsVarargsCollector(target, arrayType); } - static class AsVarargsCollector extends MethodHandle { + private static final class AsVarargsCollector extends DelegatingMethodHandle { private final MethodHandle target; private final Class arrayType; private @Stable MethodHandle asCollectorCache; - AsVarargsCollector(MethodHandle target, MethodType type, Class arrayType) { - super(type, reinvokerForm(target)); + AsVarargsCollector(MethodHandle target, Class arrayType) { + this(target.type(), target, arrayType); + } + AsVarargsCollector(MethodType type, MethodHandle target, Class arrayType) { + super(type, target); this.target = target; this.arrayType = arrayType; this.asCollectorCache = target.asCollector(arrayType, 0); } - @Override MethodHandle reinvokerTarget() { return target; } - @Override public boolean isVarargsCollector() { return true; } @Override + protected MethodHandle getTarget() { + return target; + } + + @Override public MethodHandle asFixedArity() { return target; } @Override + MethodHandle setVarargs(MemberName member) { + if (member.isVarargs()) return this; + return asFixedArity(); + } + + @Override public MethodHandle asTypeUncached(MethodType newType) { MethodType type = this.type(); int collectArg = type.parameterCount() - 1; @@ -416,32 +428,6 @@ : Arrays.asList(this, newType); return true; } - - @Override - MethodHandle setVarargs(MemberName member) { - if (member.isVarargs()) return this; - return asFixedArity(); - } - - @Override - MemberName internalMemberName() { - return asFixedArity().internalMemberName(); - } - @Override - Class internalCallerClass() { - return asFixedArity().internalCallerClass(); - } - - /*non-public*/ - @Override - boolean isInvokeSpecial() { - return asFixedArity().isInvokeSpecial(); - } - - @Override - MethodHandle copyWith(MethodType mt, LambdaForm lf) { - throw newIllegalArgumentException("do not use this"); - } } /** Factory method: Spread selected argument. */ @@ -972,7 +958,7 @@ /** This subclass allows a wrapped method handle to be re-associated with an arbitrary member name. */ - static class WrappedMember extends MethodHandle { + private static final class WrappedMember extends DelegatingMethodHandle { private final MethodHandle target; private final MemberName member; private final Class callerClass; @@ -981,7 +967,7 @@ private WrappedMember(MethodHandle target, MethodType type, MemberName member, boolean isInvokeSpecial, Class callerClass) { - super(type, reinvokerForm(target)); + super(type, target); this.target = target; this.member = member; this.callerClass = callerClass; @@ -989,16 +975,6 @@ } @Override - MethodHandle reinvokerTarget() { - 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; } @@ -1010,10 +986,15 @@ boolean isInvokeSpecial() { return isInvokeSpecial; } - @Override - MethodHandle copyWith(MethodType mt, LambdaForm lf) { - throw newIllegalArgumentException("do not use this"); + protected MethodHandle getTarget() { + 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); } } diff --git a/src/share/classes/java/lang/invoke/MethodTypeForm.java b/src/share/classes/java/lang/invoke/MethodTypeForm.java --- a/src/share/classes/java/lang/invoke/MethodTypeForm.java +++ b/src/share/classes/java/lang/invoke/MethodTypeForm.java @@ -70,8 +70,8 @@ LF_INVINTERFACE = 4, LF_INVSTATIC_INIT = 5, // DMH invokeStatic with barrier LF_INTERPRET = 6, // LF interpreter - LF_COUNTER = 7, // CMH wrapper - LF_REINVOKE = 8, // other wrapper + LF_REBIND = 7, // BoundMethodHandle + LF_DELEGATE = 8, // DelegatingMethodHandle LF_EX_LINKER = 9, // invokeExact_MT (for invokehandle) LF_EX_INVOKER = 10, // MHs.invokeExact LF_GEN_LINKER = 11, // generic invoke_MT (for invokehandle) diff --git a/src/share/classes/java/lang/invoke/SimpleMethodHandle.java b/src/share/classes/java/lang/invoke/SimpleMethodHandle.java --- a/src/share/classes/java/lang/invoke/SimpleMethodHandle.java +++ b/src/share/classes/java/lang/invoke/SimpleMethodHandle.java @@ -25,21 +25,77 @@ package java.lang.invoke; +import static java.lang.invoke.LambdaForm.BasicType.*; +import static java.lang.invoke.MethodHandleStatics.*; + /** * A method handle whose behavior is determined only by its LambdaForm. * @author jrose */ -final class SimpleMethodHandle extends MethodHandle { +final class SimpleMethodHandle extends BoundMethodHandle { private SimpleMethodHandle(MethodType type, LambdaForm form) { super(type, form); } - /*non-public*/ static SimpleMethodHandle make(MethodType type, LambdaForm form) { + /*non-public*/ static BoundMethodHandle make(MethodType type, LambdaForm form) { return new SimpleMethodHandle(type, form); } + /*non-public*/ static final SpeciesData SPECIES_DATA = SpeciesData.EMPTY; + + /*non-public*/ public SpeciesData speciesData() { + return SPECIES_DATA; + } + @Override - /*non-public*/ SimpleMethodHandle copyWith(MethodType mt, LambdaForm lf) { + /*non-public*/ BoundMethodHandle copyWith(MethodType mt, LambdaForm lf) { return make(mt, lf); } + + @Override + String internalProperties() { + return "\n& Class="+getClass().getSimpleName(); + } + + @Override + /*non-public*/ public int fieldCount() { + return 0; + } + + @Override + /*non-public*/ final BoundMethodHandle copyWithExtendL(MethodType mt, LambdaForm lf, Object narg) { + return BoundMethodHandle.bindSingle(mt, lf, narg); // Use known fast path. + } + @Override + /*non-public*/ final BoundMethodHandle copyWithExtendI(MethodType mt, LambdaForm lf, int narg) { + try { + return (BoundMethodHandle) SPECIES_DATA.extendWith(I_TYPE).constructor().invokeBasic(mt, lf, narg); + } catch (Throwable ex) { + throw uncaughtException(ex); + } + } + @Override + /*non-public*/ final BoundMethodHandle copyWithExtendJ(MethodType mt, LambdaForm lf, long narg) { + try { + return (BoundMethodHandle) SPECIES_DATA.extendWith(J_TYPE).constructor().invokeBasic(mt, lf, narg); + } catch (Throwable ex) { + throw uncaughtException(ex); + } + } + @Override + /*non-public*/ final BoundMethodHandle copyWithExtendF(MethodType mt, LambdaForm lf, float narg) { + try { + return (BoundMethodHandle) SPECIES_DATA.extendWith(F_TYPE).constructor().invokeBasic(mt, lf, narg); + } catch (Throwable ex) { + throw uncaughtException(ex); + } + } + @Override + /*non-public*/ final BoundMethodHandle copyWithExtendD(MethodType mt, LambdaForm lf, double narg) { + try { + return (BoundMethodHandle) SPECIES_DATA.extendWith(D_TYPE).constructor().invokeBasic(mt, lf, narg); + } catch (Throwable ex) { + throw uncaughtException(ex); + } + } }