changeset 9985:c7be76a1dda5

8050166: Get rid of some package-private methods on arguments in j.l.i.MethodHandle Reviewed-by: vlivanov, psandoz Contributed-by: john.r.rose@oracle.com
author vlivanov
date Wed, 10 Sep 2014 18:34:02 +0400
parents f66dc99dac52
children 9cfb4b22a01e
files src/share/classes/java/lang/invoke/BoundMethodHandle.java src/share/classes/java/lang/invoke/CallSite.java src/share/classes/java/lang/invoke/DirectMethodHandle.java src/share/classes/java/lang/invoke/MethodHandle.java src/share/classes/java/lang/invoke/MethodHandleImpl.java src/share/classes/java/lang/invoke/MethodHandles.java src/share/classes/java/lang/invoke/MethodType.java src/share/classes/java/lang/invoke/SimpleMethodHandle.java
diffstat 8 files changed, 152 insertions(+), 200 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/java/lang/invoke/BoundMethodHandle.java	Wed Sep 10 18:34:02 2014 +0400
+++ b/src/share/classes/java/lang/invoke/BoundMethodHandle.java	Wed Sep 10 18:34:02 2014 +0400
@@ -60,13 +60,12 @@
     // BMH API and internals
     //
 
-    static MethodHandle bindSingle(MethodType type, LambdaForm form, BasicType xtype, Object x) {
+    static BoundMethodHandle bindSingle(MethodType type, LambdaForm form, BasicType xtype, Object x) {
         // for some type signatures, there exist pre-defined concrete BMH classes
         try {
             switch (xtype) {
             case L_TYPE:
-                if (true)  return bindSingle(type, form, x);  // Use known fast path.
-                return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(L_TYPE).constructor().invokeBasic(type, form, x);
+                return bindSingle(type, form, x);  // Use known fast path.
             case I_TYPE:
                 return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(I_TYPE).constructor().invokeBasic(type, form, ValueConversions.widenSubword(x));
             case J_TYPE:
@@ -82,49 +81,40 @@
         }
     }
 
-    static MethodHandle bindSingle(MethodType type, LambdaForm form, Object x) {
-            return new Species_L(type, form, x);
-    }
-
-    MethodHandle cloneExtend(MethodType type, LambdaForm form, BasicType xtype, Object x) {
-        try {
-            switch (xtype) {
-            case L_TYPE: return copyWithExtendL(type, form, x);
-            case I_TYPE: return copyWithExtendI(type, form, ValueConversions.widenSubword(x));
-            case J_TYPE: return copyWithExtendJ(type, form, (long) x);
-            case F_TYPE: return copyWithExtendF(type, form, (float) x);
-            case D_TYPE: return copyWithExtendD(type, form, (double) x);
-            }
-        } catch (Throwable t) {
-            throw newInternalError(t);
-        }
-        throw newInternalError("unexpected type: " + xtype);
+    static BoundMethodHandle bindSingle(MethodType type, LambdaForm form, Object x) {
+        return Species_L.make(type, form, x);
     }
 
-    @Override
-    MethodHandle bindArgument(int pos, BasicType basicType, Object value) {
+    @Override // there is a default binder in the super class, for 'L' types only
+    /*non-public*/
+    BoundMethodHandle bindArgumentL(int pos, Object value) {
+        MethodType type = type().dropParameterTypes(pos, pos+1);
+        LambdaForm form = internalForm().bind(1+pos, speciesData());
+        return copyWithExtendL(type, form, value);
+    }
+    /*non-public*/
+    BoundMethodHandle bindArgumentI(int pos, int value) {
         MethodType type = type().dropParameterTypes(pos, pos+1);
         LambdaForm form = internalForm().bind(1+pos, speciesData());
-        return cloneExtend(type, form, basicType, value);
+        return copyWithExtendI(type, form, value);
+    }
+    /*non-public*/
+    BoundMethodHandle bindArgumentJ(int pos, long value) {
+        MethodType type = type().dropParameterTypes(pos, pos+1);
+        LambdaForm form = internalForm().bind(1+pos, speciesData());
+        return copyWithExtendJ(type, form, value);
     }
-
-    @Override
-    MethodHandle dropArguments(MethodType srcType, int pos, int drops) {
-        LambdaForm form = internalForm().addArguments(pos, srcType.parameterList().subList(pos, pos + drops));
-        try {
-             return copyWith(srcType, form);
-         } catch (Throwable t) {
-             throw newInternalError(t);
-         }
+    /*non-public*/
+    BoundMethodHandle bindArgumentF(int pos, float value) {
+        MethodType type = type().dropParameterTypes(pos, pos+1);
+        LambdaForm form = internalForm().bind(1+pos, speciesData());
+        return copyWithExtendF(type, form, value);
     }
-
-    @Override
-    MethodHandle permuteArguments(MethodType newType, int[] reorder) {
-        try {
-             return copyWith(newType, form.permuteArguments(1, reorder, basicTypes(newType.parameterList())));
-         } catch (Throwable t) {
-             throw newInternalError(t);
-         }
+    /*non-public*/
+    BoundMethodHandle bindArgumentD(int pos, double value) {
+        MethodType type = type().dropParameterTypes(pos, pos + 1);
+        LambdaForm form = internalForm().bind(1+pos, speciesData());
+        return copyWithExtendD(type, form, value);
     }
 
     /**
--- a/src/share/classes/java/lang/invoke/CallSite.java	Wed Sep 10 18:34:02 2014 +0400
+++ b/src/share/classes/java/lang/invoke/CallSite.java	Wed Sep 10 18:34:02 2014 +0400
@@ -211,7 +211,7 @@
     public abstract MethodHandle dynamicInvoker();
 
     /*non-public*/ MethodHandle makeDynamicInvoker() {
-        MethodHandle getTarget = GET_TARGET.bindReceiver(this);
+        MethodHandle getTarget = GET_TARGET.bindArgumentL(0, this);
         MethodHandle invoker = MethodHandles.exactInvoker(this.type());
         return MethodHandles.foldArguments(invoker, getTarget);
     }
--- a/src/share/classes/java/lang/invoke/DirectMethodHandle.java	Wed Sep 10 18:34:02 2014 +0400
+++ b/src/share/classes/java/lang/invoke/DirectMethodHandle.java	Wed Sep 10 18:34:02 2014 +0400
@@ -142,45 +142,8 @@
         return member;
     }
 
-    @Override
-    MethodHandle bindArgument(int pos, BasicType basicType, Object value) {
-        // If the member needs dispatching, do so.
-        if (pos == 0 && basicType == L_TYPE) {
-            DirectMethodHandle concrete = maybeRebind(value);
-            if (concrete != null)
-                return concrete.bindReceiver(value);
-        }
-        return super.bindArgument(pos, basicType, value);
-    }
-
-    @Override
-    MethodHandle bindReceiver(Object receiver) {
-        // If the member needs dispatching, do so.
-        DirectMethodHandle concrete = maybeRebind(receiver);
-        if (concrete != null)
-            return concrete.bindReceiver(receiver);
-        return super.bindReceiver(receiver);
-    }
-
     private static final MemberName.Factory IMPL_NAMES = MemberName.getFactory();
 
-    private DirectMethodHandle maybeRebind(Object receiver) {
-        if (receiver != null) {
-            switch (member.getReferenceKind()) {
-            case REF_invokeInterface:
-            case REF_invokeVirtual:
-                // Pre-dispatch the member.
-                Class<?> concreteClass = receiver.getClass();
-                MemberName concrete = new MemberName(concreteClass, member.getName(), member.getMethodType(), REF_invokeSpecial);
-                concrete = IMPL_NAMES.resolveOrNull(REF_invokeSpecial, concrete, concreteClass);
-                if (concrete != null)
-                    return new DirectMethodHandle(type(), preparedLambdaForm(concrete), concrete);
-                break;
-            }
-        }
-        return null;
-    }
-
     /**
      * Create a LF which can invoke the given method.
      * Cache and share this structure among all methods with
--- a/src/share/classes/java/lang/invoke/MethodHandle.java	Wed Sep 10 18:34:02 2014 +0400
+++ b/src/share/classes/java/lang/invoke/MethodHandle.java	Wed Sep 10 18:34:02 2014 +0400
@@ -774,7 +774,7 @@
     /*non-public*/ MethodHandle asTypeUncached(MethodType newType) {
         if (!type.isConvertibleTo(newType))
             throw new WrongMethodTypeException("cannot convert "+this+" to "+newType);
-        return asTypeCache = convertArguments(newType);
+        return asTypeCache = MethodHandleImpl.makePairwiseConvert(this, newType, 1);
     }
 
     /**
@@ -987,7 +987,7 @@
         int collectArgPos = type().parameterCount()-1;
         MethodHandle target = this;
         if (arrayType != type().parameterType(collectArgPos))
-            target = convertArguments(type().changeParameterType(collectArgPos, arrayType));
+            target = MethodHandleImpl.makePairwiseConvert(this, type().changeParameterType(collectArgPos, arrayType), 1);
         MethodHandle collector = MethodHandleImpl.varargsArray(arrayType, arrayLength);
         return MethodHandles.collectArguments(target, collectArgPos, collector);
     }
@@ -1260,14 +1260,8 @@
      * @see MethodHandles#insertArguments
      */
     public MethodHandle bindTo(Object x) {
-        Class<?> ptype;
-        @SuppressWarnings("LocalVariableHidesMemberVariable")
-        MethodType type = type();
-        if (type.parameterCount() == 0 ||
-            (ptype = type.parameterType(0)).isPrimitive())
-            throw newIllegalArgumentException("no leading reference parameter", x);
-        x = ptype.cast(x);  // throw CCE if needed
-        return bindReceiver(x);
+        x = type.leadingReferenceParameter().cast(x);  // throw CCE if needed
+        return bindArgumentL(0, x);
     }
 
     /**
@@ -1306,6 +1300,10 @@
 
     // Other transforms to do:  convert, explicitCast, permute, drop, filter, fold, GWT, catch
 
+    BoundMethodHandle bindArgumentL(int pos, Object value) {
+        return rebind().bindArgumentL(pos, value);
+    }
+
     /*non-public*/
     MethodHandle setVarargs(MemberName member) throws IllegalAccessException {
         if (!member.isVarargs())  return this;
@@ -1374,37 +1372,8 @@
     //// Sub-classes can override these default implementations.
     //// All these methods assume arguments are already validated.
 
-    /*non-public*/ MethodHandle convertArguments(MethodType newType) {
-        // Override this if it can be improved.
-        return MethodHandleImpl.makePairwiseConvert(this, newType, 1);
-    }
-
     /*non-public*/
-    MethodHandle bindArgument(int pos, BasicType basicType, Object value) {
-        // Override this if it can be improved.
-        return rebind().bindArgument(pos, basicType, value);
-    }
-
-    /*non-public*/
-    MethodHandle bindReceiver(Object receiver) {
-        // Override this if it can be improved.
-        return bindArgument(0, L_TYPE, receiver);
-    }
-
-    /*non-public*/
-    MethodHandle dropArguments(MethodType srcType, int pos, int drops) {
-        // Override this if it can be improved.
-        return rebind().dropArguments(srcType, pos, drops);
-    }
-
-    /*non-public*/
-    MethodHandle permuteArguments(MethodType newType, int[] reorder) {
-        // Override this if it can be improved.
-        return rebind().permuteArguments(newType, reorder);
-    }
-
-    /*non-public*/
-    MethodHandle rebind() {
+    BoundMethodHandle rebind() {
         // Bind 'this' into a new invoker, of the known class BMH.
         MethodType type2 = type();
         LambdaForm form2 = reinvokerForm(this);
--- a/src/share/classes/java/lang/invoke/MethodHandleImpl.java	Wed Sep 10 18:34:02 2014 +0400
+++ b/src/share/classes/java/lang/invoke/MethodHandleImpl.java	Wed Sep 10 18:34:02 2014 +0400
@@ -434,27 +434,6 @@
         boolean isInvokeSpecial() {
             return asFixedArity().isInvokeSpecial();
         }
-
-
-        @Override
-        MethodHandle bindArgument(int pos, BasicType basicType, Object value) {
-            return asFixedArity().bindArgument(pos, basicType, value);
-        }
-
-        @Override
-        MethodHandle bindReceiver(Object receiver) {
-            return asFixedArity().bindReceiver(receiver);
-        }
-
-        @Override
-        MethodHandle dropArguments(MethodType srcType, int pos, int drops) {
-            return asFixedArity().dropArguments(srcType, pos, drops);
-        }
-
-        @Override
-        MethodHandle permuteArguments(MethodType newType, int[] reorder) {
-            return asFixedArity().permuteArguments(newType, reorder);
-        }
     }
 
     /** Factory method:  Spread selected argument. */
@@ -794,7 +773,9 @@
         assert(Throwable.class.isAssignableFrom(type.parameterType(0)));
         int arity = type.parameterCount();
         if (arity > 1) {
-            return throwException(type.dropParameterTypes(1, arity)).dropArguments(type, 1, arity-1);
+            MethodHandle mh = throwException(type.dropParameterTypes(1, arity));
+            mh = MethodHandles.dropArguments(mh, 1, type.parameterList().subList(1, arity));
+            return mh;
         }
         return makePairwiseConvert(Lazy.NF_throwException.resolvedHandle(), type, 2);
     }
--- a/src/share/classes/java/lang/invoke/MethodHandles.java	Wed Sep 10 18:34:02 2014 +0400
+++ b/src/share/classes/java/lang/invoke/MethodHandles.java	Wed Sep 10 18:34:02 2014 +0400
@@ -26,6 +26,7 @@
 package java.lang.invoke;
 
 import java.lang.reflect.*;
+import java.util.BitSet;
 import java.util.List;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -1144,7 +1145,7 @@
             Class<? extends Object> refc = receiver.getClass(); // may get NPE
             MemberName method = resolveOrFail(REF_invokeSpecial, refc, name, type);
             MethodHandle mh = getDirectMethodNoRestrict(REF_invokeSpecial, refc, method, findBoundCallerClass(method));
-            return mh.bindReceiver(receiver).setVarargs(method);
+            return mh.bindArgumentL(0, receiver).setVarargs(method);
         }
 
         /**
@@ -2087,11 +2088,55 @@
     public static
     MethodHandle permuteArguments(MethodHandle target, MethodType newType, int... reorder) {
         reorder = reorder.clone();
-        checkReorder(reorder, newType, target.type());
-        return target.permuteArguments(newType, reorder);
+        permuteArgumentChecks(reorder, newType, target.type());
+        // first detect dropped arguments and handle them separately
+        MethodHandle originalTarget = target;
+        int newArity = newType.parameterCount();
+        for (int dropIdx; (dropIdx = findFirstDrop(reorder, newArity)) >= 0; ) {
+            // dropIdx is missing from reorder; add it in at the end
+            int oldArity = reorder.length;
+            target = dropArguments(target, oldArity, newType.parameterType(dropIdx));
+            reorder = Arrays.copyOf(reorder, oldArity+1);
+            reorder[oldArity] = dropIdx;
+        }
+        assert(target == originalTarget || permuteArgumentChecks(reorder, newType, target.type()));
+        // Note:  This may cache too many distinct LFs. Consider backing off to varargs code.
+        BoundMethodHandle result = target.rebind();
+        LambdaForm form = result.form.permuteArguments(1, reorder, basicTypes(newType.parameterList()));
+        return result.copyWith(newType, form);
     }
 
-    private static void checkReorder(int[] reorder, MethodType newType, MethodType oldType) {
+    /** Return the first value in [0..newArity-1] that is not present in reorder. */
+    private static int findFirstDrop(int[] reorder, int newArity) {
+        final int BIT_LIMIT = 63;  // max number of bits in bit mask
+        if (newArity < BIT_LIMIT) {
+            long mask = 0;
+            for (int arg : reorder) {
+                assert(arg < newArity);
+                mask |= (1 << arg);
+            }
+            if (mask == (1 << newArity) - 1) {
+                assert(Long.numberOfTrailingZeros(Long.lowestOneBit(~mask)) == newArity);
+                return -1;
+            }
+            // find first zero
+            long zeroBit = Long.lowestOneBit(~mask);
+            int zeroPos = Long.numberOfTrailingZeros(zeroBit);
+            assert(zeroPos < newArity);
+            return zeroPos;
+        }
+        BitSet mask = new BitSet(newArity);
+        for (int arg : reorder) {
+            assert(arg < newArity);
+            mask.set(arg);
+        }
+        int zeroPos = mask.nextClearBit(0);
+        if (zeroPos == newArity)
+            return -1;
+        return zeroPos;
+    }
+
+    private static boolean permuteArgumentChecks(int[] reorder, MethodType newType, MethodType oldType) {
         if (newType.returnType() != oldType.returnType())
             throw newIllegalArgumentException("return types do not match",
                     oldType, newType);
@@ -2109,7 +2154,7 @@
                     throw newIllegalArgumentException("parameter types do not match after reorder",
                             oldType, newType);
             }
-            if (!bad)  return;
+            if (!bad)  return true;
         }
         throw newIllegalArgumentException("bad reorder array: "+Arrays.toString(reorder));
     }
@@ -2193,6 +2238,37 @@
     public static
     MethodHandle insertArguments(MethodHandle target, int pos, Object... values) {
         int insCount = values.length;
+        Class<?>[] ptypes = insertArgumentsChecks(target, insCount, pos);
+        if (insCount == 0)  return target;
+        BoundMethodHandle result = target.rebind();
+        for (int i = 0; i < insCount; i++) {
+            Object value = values[i];
+            Class<?> ptype = ptypes[pos+i];
+            if (ptype.isPrimitive()) {
+                result = insertArgumentPrimitive(result, pos, ptype, value);
+            } else {
+                value = ptype.cast(value);  // throw CCE if needed
+                result = result.bindArgumentL(pos, value);
+            }
+        }
+        return result;
+    }
+
+    private static BoundMethodHandle insertArgumentPrimitive(BoundMethodHandle result, int pos,
+                                                             Class<?> ptype, Object value) {
+        Wrapper w = Wrapper.forPrimitiveType(ptype);
+        // perform unboxing and/or primitive conversion
+        value = w.convert(value, ptype);
+        switch (w) {
+        case INT:     return result.bindArgumentI(pos, (int)value);
+        case LONG:    return result.bindArgumentJ(pos, (long)value);
+        case FLOAT:   return result.bindArgumentF(pos, (float)value);
+        case DOUBLE:  return result.bindArgumentD(pos, (double)value);
+        default:      return result.bindArgumentI(pos, ValueConversions.widenSubword(value));
+        }
+    }
+
+    private static Class<?>[] insertArgumentsChecks(MethodHandle target, int insCount, int pos) throws RuntimeException {
         MethodType oldType = target.type();
         int outargs = oldType.parameterCount();
         int inargs  = outargs - insCount;
@@ -2200,31 +2276,7 @@
             throw newIllegalArgumentException("too many values to insert");
         if (pos < 0 || pos > inargs)
             throw newIllegalArgumentException("no argument type to append");
-        MethodHandle result = target;
-        for (int i = 0; i < insCount; i++) {
-            Object value = values[i];
-            Class<?> ptype = oldType.parameterType(pos+i);
-            if (ptype.isPrimitive()) {
-                BasicType btype = I_TYPE;
-                Wrapper w = Wrapper.forPrimitiveType(ptype);
-                switch (w) {
-                case LONG:    btype = J_TYPE; break;
-                case FLOAT:   btype = F_TYPE; break;
-                case DOUBLE:  btype = D_TYPE; break;
-                }
-                // perform unboxing and/or primitive conversion
-                value = w.convert(value, ptype);
-                result = result.bindArgument(pos, btype, value);
-                continue;
-            }
-            value = ptype.cast(value);  // throw CCE if needed
-            if (pos == 0) {
-                result = result.bindReceiver(value);
-            } else {
-                result = result.bindArgument(pos, L_TYPE, value);
-            }
-        }
-        return result;
+        return oldType.ptypes();
     }
 
     /**
@@ -2272,18 +2324,26 @@
     public static
     MethodHandle dropArguments(MethodHandle target, int pos, List<Class<?>> valueTypes) {
         MethodType oldType = target.type();  // get NPE
+        int dropped = dropArgumentChecks(oldType, pos, valueTypes);
+        if (dropped == 0)  return target;
+        BoundMethodHandle result = target.rebind();
+        LambdaForm lform = result.form;
+        lform = lform.addArguments(pos, valueTypes);
+        MethodType newType = oldType.insertParameterTypes(pos, valueTypes);
+        result = result.copyWith(newType, lform);
+        return result;
+    }
+
+    private static int dropArgumentChecks(MethodType oldType, int pos, List<Class<?>> valueTypes) {
         int dropped = valueTypes.size();
         MethodType.checkSlotCount(dropped);
-        if (dropped == 0)  return target;
         int outargs = oldType.parameterCount();
         int inargs  = outargs + dropped;
-        if (pos < 0 || pos >= inargs)
-            throw newIllegalArgumentException("no argument type to remove");
-        ArrayList<Class<?>> ptypes = new ArrayList<>(oldType.parameterList());
-        ptypes.addAll(pos, valueTypes);
-        if (ptypes.size() != inargs)  throw newIllegalArgumentException("valueTypes");
-        MethodType newType = MethodType.methodType(oldType.returnType(), ptypes);
-        return target.dropArguments(newType, pos, dropped);
+        if (pos < 0 || pos > outargs)
+            throw newIllegalArgumentException("no argument type to remove"
+                    + Arrays.asList(oldType, pos, valueTypes, inargs, outargs)
+                    );
+        return dropped;
     }
 
     /**
--- a/src/share/classes/java/lang/invoke/MethodType.java	Wed Sep 10 18:34:02 2014 +0400
+++ b/src/share/classes/java/lang/invoke/MethodType.java	Wed Sep 10 18:34:02 2014 +0400
@@ -498,6 +498,17 @@
         return this;  // arguments check out; no change
     }
 
+    /** Return the leading parameter type, which must exist and be a reference.
+     *  @return the leading parameter type, after error checks
+     */
+    /*non-public*/ Class<?> leadingReferenceParameter() {
+        Class<?> ptype;
+        if (ptypes.length == 0 ||
+            (ptype = ptypes[0]).isPrimitive())
+            throw newIllegalArgumentException("no leading reference parameter");
+        return ptype;
+    }
+
     /**
      * Finds or creates a method type with some parameter types omitted.
      * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
--- a/src/share/classes/java/lang/invoke/SimpleMethodHandle.java	Wed Sep 10 18:34:02 2014 +0400
+++ b/src/share/classes/java/lang/invoke/SimpleMethodHandle.java	Wed Sep 10 18:34:02 2014 +0400
@@ -25,9 +25,6 @@
 
 package java.lang.invoke;
 
-import static java.lang.invoke.LambdaForm.*;
-import static java.lang.invoke.LambdaForm.BasicType.*;
-
 /**
  * A method handle whose behavior is determined only by its LambdaForm.
  * @author jrose
@@ -40,23 +37,4 @@
     /*non-public*/ static SimpleMethodHandle make(MethodType type, LambdaForm form) {
         return new SimpleMethodHandle(type, form);
     }
-
-    @Override
-    MethodHandle bindArgument(int pos, BasicType basicType, Object value) {
-        MethodType type2 = type().dropParameterTypes(pos, pos+1);
-        LambdaForm form2 = internalForm().bind(1+pos, BoundMethodHandle.SpeciesData.EMPTY);
-        return BoundMethodHandle.bindSingle(type2, form2, basicType, value);
-    }
-
-    @Override
-    MethodHandle dropArguments(MethodType srcType, int pos, int drops) {
-        LambdaForm newForm = internalForm().addArguments(pos, srcType.parameterList().subList(pos, pos+drops));
-        return new SimpleMethodHandle(srcType, newForm);
-    }
-
-    @Override
-    MethodHandle permuteArguments(MethodType newType, int[] reorder) {
-        LambdaForm form2 = internalForm().permuteArguments(1, reorder, basicTypes(newType.parameterList()));
-        return new SimpleMethodHandle(newType, form2);
-    }
 }