changeset 9985:bae3f8ea54a1

8050057: Improve caching of MethodHandle reinvokers Reviewed-by: vlivanov, psandoz Contributed-by: john.r.rose@oracle.com
author vlivanov
date Wed, 10 Sep 2014 18:34:03 +0400
parents d33546256199
children 4e6337ca7989
files src/share/classes/java/lang/invoke/BoundMethodHandle.java src/share/classes/java/lang/invoke/DelegatingMethodHandle.java src/share/classes/java/lang/invoke/DirectMethodHandle.java src/share/classes/java/lang/invoke/LambdaForm.java src/share/classes/java/lang/invoke/MethodHandle.java src/share/classes/java/lang/invoke/MethodHandleImpl.java src/share/classes/java/lang/invoke/MethodTypeForm.java src/share/classes/java/lang/invoke/SimpleMethodHandle.java
diffstat 8 files changed, 271 insertions(+), 132 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/java/lang/invoke/BoundMethodHandle.java	Wed Sep 10 18:34:03 2014 +0400
+++ b/src/share/classes/java/lang/invoke/BoundMethodHandle.java	Wed Sep 10 18:34:03 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/src/share/classes/java/lang/invoke/DelegatingMethodHandle.java	Wed Sep 10 18:34:03 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/src/share/classes/java/lang/invoke/DirectMethodHandle.java	Wed Sep 10 18:34:03 2014 +0400
+++ b/src/share/classes/java/lang/invoke/DirectMethodHandle.java	Wed Sep 10 18:34:03 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/src/share/classes/java/lang/invoke/LambdaForm.java	Wed Sep 10 18:34:03 2014 +0400
+++ b/src/share/classes/java/lang/invoke/LambdaForm.java	Wed Sep 10 18:34:03 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/src/share/classes/java/lang/invoke/MethodHandle.java	Wed Sep 10 18:34:03 2014 +0400
+++ b/src/share/classes/java/lang/invoke/MethodHandle.java	Wed Sep 10 18:34:03 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/src/share/classes/java/lang/invoke/MethodHandleImpl.java	Wed Sep 10 18:34:03 2014 +0400
+++ b/src/share/classes/java/lang/invoke/MethodHandleImpl.java	Wed Sep 10 18:34:03 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
-        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);
         }
     }
 
--- a/src/share/classes/java/lang/invoke/MethodTypeForm.java	Wed Sep 10 18:34:03 2014 +0400
+++ b/src/share/classes/java/lang/invoke/MethodTypeForm.java	Wed Sep 10 18:34:03 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/src/share/classes/java/lang/invoke/SimpleMethodHandle.java	Wed Sep 10 18:34:03 2014 +0400
+++ b/src/share/classes/java/lang/invoke/SimpleMethodHandle.java	Wed Sep 10 18:34:03 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*/ 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);
+        }
+    }
 }