8050057: Improve caching of MethodHandle reinvokers
Reviewed-by: vlivanov, psandoz
Contributed-by: john.r.rose@oracle.com
--- a/jdk/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java Wed Sep 10 19:19:49 2014 +0400
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java Wed Sep 10 19:19:49 2014 +0400
@@ -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();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/DelegatingMethodHandle.java Wed Sep 10 19:19:49 2014 +0400
@@ -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);
+ }
+ }
+}
--- a/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java Wed Sep 10 19:19:49 2014 +0400
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java Wed Sep 10 19:19:49 2014 +0400
@@ -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);
--- a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java Wed Sep 10 19:19:49 2014 +0400
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java Wed Sep 10 19:19:49 2014 +0400
@@ -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());
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java Wed Sep 10 19:19:49 2014 +0400
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java Wed Sep 10 19:19:49 2014 +0400
@@ -1393,66 +1393,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.
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java Wed Sep 10 19:19:49 2014 +0400
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java Wed Sep 10 19:19:49 2014 +0400
@@ -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
+ protected MethodHandle getTarget() {
+ return target;
+ }
@Override
- MethodHandle copyWith(MethodType mt, LambdaForm lf) {
- throw newIllegalArgumentException("do not use this");
+ 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);
}
}
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodTypeForm.java Wed Sep 10 19:19:49 2014 +0400
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodTypeForm.java Wed Sep 10 19:19:49 2014 +0400
@@ -70,8 +70,8 @@
LF_INVINTERFACE = 4,
LF_INVSTATIC_INIT = 5, // DMH invokeStatic with <clinit> 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)
--- a/jdk/src/java.base/share/classes/java/lang/invoke/SimpleMethodHandle.java Wed Sep 10 19:19:49 2014 +0400
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/SimpleMethodHandle.java Wed Sep 10 19:19:49 2014 +0400
@@ -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*/ 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*/ SimpleMethodHandle copyWith(MethodType mt, LambdaForm lf) {
- return make(mt, lf);
+ /*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);
+ }
}
}