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
--- a/jdk/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java Wed Sep 10 19:19:48 2014 +0400
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java Wed Sep 10 19:19:48 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/jdk/src/java.base/share/classes/java/lang/invoke/CallSite.java Wed Sep 10 19:19:48 2014 +0400
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/CallSite.java Wed Sep 10 19:19:48 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/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java Wed Sep 10 19:19:48 2014 +0400
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java Wed Sep 10 19:19:48 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/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java Wed Sep 10 19:19:48 2014 +0400
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java Wed Sep 10 19:19:48 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/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java Wed Sep 10 19:19:48 2014 +0400
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java Wed Sep 10 19:19:48 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/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Wed Sep 10 19:19:48 2014 +0400
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Wed Sep 10 19:19:48 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;
@@ -1143,7 +1144,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);
}
/**
@@ -2086,11 +2087,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);
@@ -2108,7 +2153,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));
}
@@ -2192,6 +2237,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;
@@ -2199,31 +2275,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();
}
/**
@@ -2271,18 +2323,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/jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java Wed Sep 10 19:19:48 2014 +0400
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java Wed Sep 10 19:19:48 2014 +0400
@@ -499,6 +499,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/jdk/src/java.base/share/classes/java/lang/invoke/SimpleMethodHandle.java Wed Sep 10 19:19:48 2014 +0400
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/SimpleMethodHandle.java Wed Sep 10 19:19:48 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);
- }
}