7191102: nightly failures after JSR 292 lazy method handle update (round 3)
Reviewed-by: twisti, kvn
diff --git a/jdk/src/share/classes/java/lang/invoke/BoundMethodHandle.java b/jdk/src/share/classes/java/lang/invoke/BoundMethodHandle.java
index c5818d9..4071305 100644
--- a/jdk/src/share/classes/java/lang/invoke/BoundMethodHandle.java
+++ b/jdk/src/share/classes/java/lang/invoke/BoundMethodHandle.java
@@ -143,6 +143,11 @@
protected abstract SpeciesData speciesData();
@Override
+ final Object internalProperties() {
+ return "/BMH="+internalValues();
+ }
+
+ @Override
final Object internalValues() {
Object[] boundValues = new Object[speciesData().fieldCount()];
for (int i = 0; i < boundValues.length; ++i) {
diff --git a/jdk/src/share/classes/java/lang/invoke/DirectMethodHandle.java b/jdk/src/share/classes/java/lang/invoke/DirectMethodHandle.java
index 4e5d591..b1c7f82 100644
--- a/jdk/src/share/classes/java/lang/invoke/DirectMethodHandle.java
+++ b/jdk/src/share/classes/java/lang/invoke/DirectMethodHandle.java
@@ -108,8 +108,8 @@
}
@Override
- String debugString() {
- return "DMH["+member.toString()+"]="+super.debugString();
+ String internalProperties() {
+ return "/DMH="+member.toString();
}
//// Implementation methods.
diff --git a/jdk/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java b/jdk/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java
index 632ae58..48f88ad 100644
--- a/jdk/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java
+++ b/jdk/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java
@@ -185,12 +185,17 @@
}
class CpPatch {
- int index;
- Object value;
- CpPatch(int index, Object value) {
+ final int index;
+ final String placeholder;
+ final Object value;
+ CpPatch(int index, String placeholder, Object value) {
this.index = index;
+ this.placeholder = placeholder;
this.value = value;
}
+ public String toString() {
+ return "CpPatch/index="+index+",placeholder="+placeholder+",value="+value;
+ }
}
Map<Object, CpPatch> cpPatches = new HashMap<>();
@@ -205,7 +210,7 @@
}
// insert placeholder in CP and remember the patch
int index = cw.newConst((Object) cpPlaceholder); // TODO check if aready in the constant pool
- cpPatches.put(cpPlaceholder, new CpPatch(index, arg));
+ cpPatches.put(cpPlaceholder, new CpPatch(index, cpPlaceholder, arg));
return cpPlaceholder;
}
@@ -213,7 +218,9 @@
int size = getConstantPoolSize(classFile);
Object[] res = new Object[size];
for (CpPatch p : cpPatches.values()) {
- res[p.index] = p.value;
+ if (p.index >= size)
+ throw new InternalError("in cpool["+size+"]: "+p+"\n"+Arrays.toString(Arrays.copyOf(classFile, 20)));
+ res[p.index] = p.value;
}
return res;
}
diff --git a/jdk/src/share/classes/java/lang/invoke/Invokers.java b/jdk/src/share/classes/java/lang/invoke/Invokers.java
index 904a8c7..0e3dcf6 100644
--- a/jdk/src/share/classes/java/lang/invoke/Invokers.java
+++ b/jdk/src/share/classes/java/lang/invoke/Invokers.java
@@ -74,8 +74,18 @@
MethodHandle invoker = exactInvoker;
if (invoker != null) return invoker;
MethodType mtype = targetType;
- LambdaForm lform = invokeForm(mtype, MethodTypeForm.LF_EX_INVOKER);
- invoker = BoundMethodHandle.bindSingle(mtype.invokerType(), lform, mtype);
+ MethodType invokerType = mtype.invokerType();
+ LambdaForm lform;
+ final int MTYPE_ARG_APPENDED = 1; // argument count for appended mtype value
+ if (mtype.parameterSlotCount() <= MethodType.MAX_MH_INVOKER_ARITY - MTYPE_ARG_APPENDED) {
+ lform = invokeForm(mtype, false, MethodTypeForm.LF_EX_INVOKER);
+ invoker = BoundMethodHandle.bindSingle(invokerType, lform, mtype);
+ } else {
+ // At maximum arity, we cannot afford an extra mtype argument,
+ // so build a fully customized (non-cached) invoker form.
+ lform = invokeForm(mtype, true, MethodTypeForm.LF_EX_INVOKER);
+ invoker = SimpleMethodHandle.make(invokerType, lform);
+ }
assert(checkInvoker(invoker));
exactInvoker = invoker;
return invoker;
@@ -85,9 +95,20 @@
MethodHandle invoker = generalInvoker;
if (invoker != null) return invoker;
MethodType mtype = targetType;
- prepareForGenericCall(mtype);
- LambdaForm lform = invokeForm(mtype, MethodTypeForm.LF_GEN_INVOKER);
- invoker = BoundMethodHandle.bindSingle(mtype.invokerType(), lform, mtype);
+ MethodType invokerType = mtype.invokerType();
+ LambdaForm lform;
+ final int MTYPE_ARG_APPENDED = 1; // argument count for appended mtype value
+ assert(GENERIC_INVOKER_SLOP >= MTYPE_ARG_APPENDED);
+ if (mtype.parameterSlotCount() <= MethodType.MAX_MH_INVOKER_ARITY - GENERIC_INVOKER_SLOP) {
+ prepareForGenericCall(mtype);
+ lform = invokeForm(mtype, false, MethodTypeForm.LF_GEN_INVOKER);
+ invoker = BoundMethodHandle.bindSingle(invokerType, lform, mtype);
+ } else {
+ // At maximum arity, we cannot afford an extra mtype argument,
+ // so build a fully customized (non-cached) invoker form.
+ lform = invokeForm(mtype, true, MethodTypeForm.LF_GEN_INVOKER);
+ invoker = SimpleMethodHandle.make(invokerType, lform);
+ }
assert(checkInvoker(invoker));
generalInvoker = invoker;
return invoker;
@@ -102,6 +123,7 @@
}
static MemberName invokeBasicMethod(MethodType type) {
+ type = type.basicType();
String name = "invokeBasic";
try {
//Lookup.findVirtual(MethodHandle.class, name, type);
@@ -135,9 +157,31 @@
/*non-public*/ MethodHandle spreadInvoker(int leadingArgCount) {
MethodHandle vaInvoker = spreadInvokers[leadingArgCount];
if (vaInvoker != null) return vaInvoker;
- MethodHandle gInvoker = generalInvoker();
int spreadArgCount = targetType.parameterCount() - leadingArgCount;
- vaInvoker = gInvoker.asSpreader(Object[].class, spreadArgCount);
+ MethodType spreadInvokerType = targetType
+ .replaceParameterTypes(leadingArgCount, targetType.parameterCount(), Object[].class);
+ if (targetType.parameterSlotCount() <= MethodType.MAX_MH_INVOKER_ARITY) {
+ // Factor sinvoker.invoke(mh, a) into ginvoker.asSpreader().invoke(mh, a)
+ // where ginvoker.invoke(mh, a*) => mh.invoke(a*).
+ MethodHandle genInvoker = generalInvoker();
+ vaInvoker = genInvoker.asSpreader(Object[].class, spreadArgCount);
+ } else {
+ // Cannot build a general invoker here of type ginvoker.invoke(mh, a*[254]).
+ // Instead, factor sinvoker.invoke(mh, a) into ainvoker.invoke(filter(mh), a)
+ // where filter(mh) == mh.asSpreader(Object[], spreadArgCount)
+ MethodHandle arrayInvoker = MethodHandles.exactInvoker(spreadInvokerType);
+ MethodHandle makeSpreader;
+ try {
+ makeSpreader = IMPL_LOOKUP
+ .findVirtual(MethodHandle.class, "asSpreader",
+ MethodType.methodType(MethodHandle.class, Class.class, int.class));
+ } catch (ReflectiveOperationException ex) {
+ throw new InternalError(ex);
+ }
+ makeSpreader = MethodHandles.insertArguments(makeSpreader, 1, Object[].class, spreadArgCount);
+ vaInvoker = MethodHandles.filterArgument(arrayInvoker, 0, makeSpreader);
+ }
+ assert(vaInvoker.type().equals(spreadInvokerType.invokerType()));
spreadInvokers[leadingArgCount] = vaInvoker;
return vaInvoker;
}
@@ -171,7 +215,7 @@
.findStatic(CallSite.class, "uninitializedCallSite",
MethodType.methodType(Empty.class));
} catch (ReflectiveOperationException ex) {
- throw new RuntimeException(ex);
+ throw new InternalError(ex);
}
}
invoker = MethodHandles.explicitCastArguments(invoker, MethodType.methodType(targetType.returnType()));
@@ -185,30 +229,39 @@
return "Invokers"+targetType;
}
- private static MethodType fixMethodType(Class<?> callerClass, Object type) {
- if (type instanceof MethodType)
- return (MethodType) type;
- else
- return MethodType.fromMethodDescriptorString((String)type, callerClass.getClassLoader());
- }
-
- static MemberName exactInvokerMethod(Class<?> callerClass, Object type, Object[] appendixResult) {
- MethodType mtype = fixMethodType(callerClass, type);
- LambdaForm lform = invokeForm(mtype, MethodTypeForm.LF_EX_LINKER);
- appendixResult[0] = mtype;
+ static MemberName exactInvokerMethod(MethodType mtype, Object[] appendixResult) {
+ LambdaForm lform;
+ final int MTYPE_ARG_APPENDED = 1; // argument count for appended mtype value
+ if (mtype.parameterSlotCount() <= MethodType.MAX_MH_ARITY - MTYPE_ARG_APPENDED) {
+ lform = invokeForm(mtype, false, MethodTypeForm.LF_EX_LINKER);
+ appendixResult[0] = mtype;
+ } else {
+ lform = invokeForm(mtype, true, MethodTypeForm.LF_EX_LINKER);
+ }
return lform.vmentry;
}
- static MemberName genericInvokerMethod(Class<?> callerClass, Object type, Object[] appendixResult) {
- MethodType mtype = fixMethodType(callerClass, type);
- LambdaForm lform = invokeForm(mtype, MethodTypeForm.LF_GEN_LINKER);
- prepareForGenericCall(mtype);
- appendixResult[0] = mtype;
+ static MemberName genericInvokerMethod(MethodType mtype, Object[] appendixResult) {
+ LambdaForm lform;
+ final int MTYPE_ARG_APPENDED = 1; // argument count for appended mtype value
+ if (mtype.parameterSlotCount() <= MethodType.MAX_MH_ARITY - (MTYPE_ARG_APPENDED + GENERIC_INVOKER_SLOP)) {
+ lform = invokeForm(mtype, false, MethodTypeForm.LF_GEN_LINKER);
+ appendixResult[0] = mtype;
+ prepareForGenericCall(mtype);
+ } else {
+ lform = invokeForm(mtype, true, MethodTypeForm.LF_GEN_LINKER);
+ }
return lform.vmentry;
}
- private static LambdaForm invokeForm(MethodType mtype, int which) {
- mtype = mtype.basicType(); // normalize Z to I, String to Object, etc.
+ private static LambdaForm invokeForm(MethodType mtype, boolean customized, int which) {
+ boolean isCached;
+ if (!customized) {
+ mtype = mtype.basicType(); // normalize Z to I, String to Object, etc.
+ isCached = true;
+ } else {
+ isCached = false; // maybe cache if mtype == mtype.basicType()
+ }
boolean isLinker, isGeneric;
String debugName;
switch (which) {
@@ -218,27 +271,32 @@
case MethodTypeForm.LF_GEN_INVOKER: isLinker = false; isGeneric = true; debugName = "invoker"; break;
default: throw new InternalError();
}
- LambdaForm lform = mtype.form().cachedLambdaForm(which);
- if (lform != null) return lform;
+ LambdaForm lform;
+ if (isCached) {
+ lform = mtype.form().cachedLambdaForm(which);
+ if (lform != null) return lform;
+ }
// exactInvokerForm (Object,Object)Object
// link with java.lang.invoke.MethodHandle.invokeBasic(MethodHandle,Object,Object)Object/invokeSpecial
final int THIS_MH = 0;
final int CALL_MH = THIS_MH + (isLinker ? 0 : 1);
final int ARG_BASE = CALL_MH + 1;
final int OUTARG_LIMIT = ARG_BASE + mtype.parameterCount();
- final int INARG_LIMIT = OUTARG_LIMIT + (isLinker ? 1 : 0);
+ final int INARG_LIMIT = OUTARG_LIMIT + (isLinker && !customized ? 1 : 0);
int nameCursor = OUTARG_LIMIT;
- final int MTYPE_ARG = nameCursor++; // might be last in-argument
+ final int MTYPE_ARG = customized ? -1 : nameCursor++; // might be last in-argument
final int CHECK_TYPE = nameCursor++;
final int LINKER_CALL = nameCursor++;
MethodType invokerFormType = mtype.invokerType();
if (isLinker) {
- invokerFormType = invokerFormType.appendParameterTypes(MemberName.class);
+ if (!customized)
+ invokerFormType = invokerFormType.appendParameterTypes(MemberName.class);
} else {
invokerFormType = invokerFormType.invokerType();
}
Name[] names = arguments(nameCursor - INARG_LIMIT, invokerFormType);
- assert(names.length == nameCursor);
+ assert(names.length == nameCursor)
+ : Arrays.asList(mtype, customized, which, nameCursor, names.length);
if (MTYPE_ARG >= INARG_LIMIT) {
assert(names[MTYPE_ARG] == null);
names[MTYPE_ARG] = BoundMethodHandle.getSpeciesData("L").getterName(names[THIS_MH], 0);
@@ -248,31 +306,42 @@
// Make the final call. If isGeneric, then prepend the result of type checking.
MethodType outCallType;
Object[] outArgs;
+ Object mtypeArg = (customized ? mtype : names[MTYPE_ARG]);
if (!isGeneric) {
- names[CHECK_TYPE] = new Name(NF_checkExactType, names[CALL_MH], names[MTYPE_ARG]);
+ names[CHECK_TYPE] = new Name(NF_checkExactType, names[CALL_MH], mtypeArg);
// mh.invokeExact(a*):R => checkExactType(mh, TYPEOF(a*:R)); mh.invokeBasic(a*)
outArgs = Arrays.copyOfRange(names, CALL_MH, OUTARG_LIMIT, Object[].class);
outCallType = mtype;
+ } else if (customized) {
+ names[CHECK_TYPE] = new Name(NF_asType, names[CALL_MH], mtypeArg);
+ // mh.invokeGeneric(a*):R =>
+ // let mt=TYPEOF(a*:R), tmh=asType(mh, mt);
+ // tmh.invokeBasic(a*)
+ outArgs = Arrays.copyOfRange(names, CALL_MH, OUTARG_LIMIT, Object[].class);
+ outCallType = mtype;
} else {
- names[CHECK_TYPE] = new Name(NF_checkGenericType, names[CALL_MH], names[MTYPE_ARG]);
+ names[CHECK_TYPE] = new Name(NF_checkGenericType, names[CALL_MH], mtypeArg);
// mh.invokeGeneric(a*):R =>
// let mt=TYPEOF(a*:R), gamh=checkGenericType(mh, mt);
// gamh.invokeBasic(mt, mh, a*)
final int PREPEND_GAMH = 0, PREPEND_MT = 1, PREPEND_COUNT = 2;
+ assert(GENERIC_INVOKER_SLOP == PREPEND_COUNT);
outArgs = Arrays.copyOfRange(names, CALL_MH, OUTARG_LIMIT + PREPEND_COUNT, Object[].class);
// prepend arguments:
System.arraycopy(outArgs, 0, outArgs, PREPEND_COUNT, outArgs.length - PREPEND_COUNT);
outArgs[PREPEND_GAMH] = names[CHECK_TYPE];
- outArgs[PREPEND_MT] = names[MTYPE_ARG];
+ outArgs[PREPEND_MT] = mtypeArg;
outCallType = mtype.insertParameterTypes(0, MethodType.class, MethodHandle.class);
}
names[LINKER_CALL] = new Name(invokeBasicMethod(outCallType), outArgs);
lform = new LambdaForm(debugName, INARG_LIMIT, names);
if (isLinker)
lform.compileToBytecode(); // JVM needs a real methodOop
- lform = mtype.form().setCachedLambdaForm(which, lform);
+ if (isCached)
+ lform = mtype.form().setCachedLambdaForm(which, lform);
return lform;
}
+ private static final int GENERIC_INVOKER_SLOP = 2; // used elsewhere to avoid arity problems
/*non-public*/ static
WrongMethodTypeException newWrongMethodTypeException(MethodType actual, MethodType expected) {
@@ -370,6 +439,7 @@
// Local constant functions:
private static final NamedFunction NF_checkExactType;
private static final NamedFunction NF_checkGenericType;
+ private static final NamedFunction NF_asType;
private static final NamedFunction NF_getCallSiteTarget;
static {
try {
@@ -377,6 +447,8 @@
.getDeclaredMethod("checkExactType", Object.class, Object.class));
NF_checkGenericType = new NamedFunction(Invokers.class
.getDeclaredMethod("checkGenericType", Object.class, Object.class));
+ NF_asType = new NamedFunction(MethodHandle.class
+ .getDeclaredMethod("asType", MethodType.class));
NF_getCallSiteTarget = new NamedFunction(Invokers.class
.getDeclaredMethod("getCallSiteTarget", Object.class));
NF_checkExactType.resolve();
diff --git a/jdk/src/share/classes/java/lang/invoke/LambdaForm.java b/jdk/src/share/classes/java/lang/invoke/LambdaForm.java
index 2b9a038..e39896a 100644
--- a/jdk/src/share/classes/java/lang/invoke/LambdaForm.java
+++ b/jdk/src/share/classes/java/lang/invoke/LambdaForm.java
@@ -596,14 +596,7 @@
Object interpretWithArguments(Object... argumentValues) throws Throwable {
if (TRACE_INTERPRETER)
return interpretWithArgumentsTracing(argumentValues);
- if (COMPILE_THRESHOLD != 0 &&
- invocationCounter < COMPILE_THRESHOLD) {
- invocationCounter++; // benign race
- if (invocationCounter >= COMPILE_THRESHOLD) {
- // Replace vmentry with a bytecode version of this LF.
- compileToBytecode();
- }
- }
+ checkInvocationCounter();
assert(arityCheck(argumentValues));
Object[] values = Arrays.copyOf(argumentValues, names.length);
for (int i = argumentValues.length; i < values.length; i++) {
@@ -630,6 +623,16 @@
return name.function.invokeWithArguments(arguments);
}
+ private void checkInvocationCounter() {
+ if (COMPILE_THRESHOLD != 0 &&
+ invocationCounter < COMPILE_THRESHOLD) {
+ invocationCounter++; // benign race
+ if (invocationCounter >= COMPILE_THRESHOLD) {
+ // Replace vmentry with a bytecode version of this LF.
+ compileToBytecode();
+ }
+ }
+ }
Object interpretWithArgumentsTracing(Object... argumentValues) throws Throwable {
traceInterpreter("[ interpretWithArguments", this, argumentValues);
if (invocationCounter < COMPILE_THRESHOLD) {
@@ -703,7 +706,7 @@
}
public String toString() {
- StringBuilder buf = new StringBuilder("Lambda(");
+ StringBuilder buf = new StringBuilder(debugName+"=Lambda(");
for (int i = 0; i < names.length; i++) {
if (i == arity) buf.append(")=>{");
Name n = names[i];
diff --git a/jdk/src/share/classes/java/lang/invoke/MethodHandle.java b/jdk/src/share/classes/java/lang/invoke/MethodHandle.java
index 76429a8..a5e4e58 100644
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandle.java
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandle.java
@@ -924,7 +924,7 @@
if (arrayType != type().parameterType(collectArgPos))
target = convertArguments(type().changeParameterType(collectArgPos, arrayType));
MethodHandle collector = ValueConversions.varargsArray(arrayType, arrayLength);
- return MethodHandleImpl.makeCollectArguments(target, collector, collectArgPos, false);
+ return MethodHandles.collectArguments(target, collectArgPos, collector);
}
// private API: return true if last param exactly matches arrayType
@@ -1226,7 +1226,7 @@
return "MethodHandle"+type;
}
String debugString() {
- return standardString()+"="+internalForm()+internalValues();
+ return standardString()+"/LF="+internalForm()+internalProperties();
}
//// Implementation methods.
@@ -1269,6 +1269,12 @@
/*non-public*/
Object internalValues() {
+ return null;
+ }
+
+ /*non-public*/
+ Object internalProperties() {
+ // Override to something like "/FOO=bar"
return "";
}
diff --git a/jdk/src/share/classes/java/lang/invoke/MethodHandleImpl.java b/jdk/src/share/classes/java/lang/invoke/MethodHandleImpl.java
index 39dfdf2..d9822bd 100644
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandleImpl.java
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandleImpl.java
@@ -59,7 +59,7 @@
Name[] args = Arrays.copyOfRange(names, 1, 1 + srcType.parameterCount());
names[names.length - 1] = new Name(accessor.asType(srcType), (Object[]) args);
LambdaForm form = new LambdaForm("getElement", lambdaType.parameterCount(), names);
- MethodHandle mh = new SimpleMethodHandle(srcType, form);
+ MethodHandle mh = SimpleMethodHandle.make(srcType, form);
if (ArrayAccessor.needCast(arrayClass)) {
mh = mh.bindTo(arrayClass);
}
@@ -171,38 +171,46 @@
// Calculate extra arguments (temporaries) required in the names array.
// FIXME: Use an ArrayList<Name>. Some arguments require more than one conversion step.
- int extra = 0;
- for (int i = 0; i < srcType.parameterCount(); i++) {
- Class<?> src = srcType.parameterType(i);
- Class<?> dst = dstType.parameterType(i);
- if (!VerifyType.isNullConversion(src, dst)) {
- extra++;
+ final int INARG_COUNT = srcType.parameterCount();
+ int conversions = 0;
+ boolean[] needConv = new boolean[1+INARG_COUNT];
+ for (int i = 0; i <= INARG_COUNT; i++) {
+ Class<?> src = (i == INARG_COUNT) ? dstType.returnType() : srcType.parameterType(i);
+ Class<?> dst = (i == INARG_COUNT) ? srcType.returnType() : dstType.parameterType(i);
+ if (!VerifyType.isNullConversion(src, dst) ||
+ level <= 1 && dst.isInterface() && !dst.isAssignableFrom(src)) {
+ needConv[i] = true;
+ conversions++;
}
}
+ boolean retConv = needConv[INARG_COUNT];
- Class<?> needReturn = srcType.returnType();
- Class<?> haveReturn = dstType.returnType();
- boolean retConv = !VerifyType.isNullConversion(haveReturn, needReturn);
+ final int IN_MH = 0;
+ final int INARG_BASE = 1;
+ final int INARG_LIMIT = INARG_BASE + INARG_COUNT;
+ final int NAME_LIMIT = INARG_LIMIT + conversions + 1;
+ final int RETURN_CONV = (!retConv ? -1 : NAME_LIMIT - 1);
+ final int OUT_CALL = (!retConv ? NAME_LIMIT : RETURN_CONV) - 1;
// Now build a LambdaForm.
- MethodType lambdaType = srcType.invokerType();
- Name[] names = arguments(extra + 1, lambdaType);
- int[] indexes = new int[lambdaType.parameterCount()];
+ MethodType lambdaType = srcType.basicType().invokerType();
+ Name[] names = arguments(NAME_LIMIT - INARG_LIMIT, lambdaType);
- MethodType midType = dstType;
- for (int i = 0, argIndex = 1, tmpIndex = lambdaType.parameterCount(); i < srcType.parameterCount(); i++, argIndex++) {
+ // Collect the arguments to the outgoing call, maybe with conversions:
+ final int OUTARG_BASE = 0; // target MH is Name.function, name Name.arguments[0]
+ Object[] outArgs = new Object[OUTARG_BASE + INARG_COUNT];
+
+ int nameCursor = INARG_LIMIT;
+ for (int i = 0; i < INARG_COUNT; i++) {
Class<?> src = srcType.parameterType(i);
- Class<?> dst = midType.parameterType(i);
+ Class<?> dst = dstType.parameterType(i);
- if (VerifyType.isNullConversion(src, dst)) {
+ if (!needConv[i]) {
// do nothing: difference is trivial
- indexes[i] = argIndex;
+ outArgs[OUTARG_BASE + i] = names[INARG_BASE + i];
continue;
}
- // Work the current type backward toward the desired caller type:
- midType = midType.changeParameterType(i, src);
-
// Tricky case analysis follows.
MethodHandle fn = null;
if (src.isPrimitive()) {
@@ -246,33 +254,41 @@
fn = ValueConversions.cast(dst);
}
}
- names[tmpIndex] = new Name(fn, names[argIndex]);
- indexes[i] = tmpIndex;
- tmpIndex++;
- }
- if (retConv) {
- MethodHandle adjustReturn;
- if (haveReturn == void.class) {
- // synthesize a zero value for the given void
- Object zero = Wrapper.forBasicType(needReturn).zero();
- adjustReturn = MethodHandles.constant(needReturn, zero);
- } else {
- MethodHandle identity = MethodHandles.identity(needReturn);
- MethodType needConversion = identity.type().changeParameterType(0, haveReturn);
- adjustReturn = makePairwiseConvert(identity, needConversion, level);
- }
- target = makeCollectArguments(adjustReturn, target, 0, false);
+ Name conv = new Name(fn, names[INARG_BASE + i]);
+ assert(names[nameCursor] == null);
+ names[nameCursor++] = conv;
+ assert(outArgs[OUTARG_BASE + i] == null);
+ outArgs[OUTARG_BASE + i] = conv;
}
// Build argument array for the call.
- Name[] targetArgs = new Name[dstType.parameterCount()];
- for (int i = 0; i < dstType.parameterCount(); i++) {
- int idx = indexes[i];
- targetArgs[i] = names[idx];
+ assert(nameCursor == OUT_CALL);
+ names[OUT_CALL] = new Name(target, outArgs);
+
+ if (RETURN_CONV < 0) {
+ assert(OUT_CALL == names.length-1);
+ } else {
+ Class<?> needReturn = srcType.returnType();
+ Class<?> haveReturn = dstType.returnType();
+ MethodHandle fn;
+ Object[] arg = { names[OUT_CALL] };
+ if (haveReturn == void.class) {
+ // synthesize a zero value for the given void
+ Object zero = Wrapper.forBasicType(needReturn).zero();
+ fn = MethodHandles.constant(needReturn, zero);
+ arg = new Object[0]; // don't pass names[OUT_CALL] to conversion
+ } else {
+ MethodHandle identity = MethodHandles.identity(needReturn);
+ MethodType needConversion = identity.type().changeParameterType(0, haveReturn);
+ fn = makePairwiseConvert(identity, needConversion, level);
+ }
+ assert(names[RETURN_CONV] == null);
+ names[RETURN_CONV] = new Name(fn, arg);
+ assert(RETURN_CONV == names.length-1);
}
- names[names.length - 1] = new Name(target, (Object[]) targetArgs);
+
LambdaForm form = new LambdaForm("convert", lambdaType.parameterCount(), names);
- return new SimpleMethodHandle(srcType, form);
+ return SimpleMethodHandle.make(srcType, form);
}
static MethodHandle makeReferenceIdentity(Class<?> refType) {
@@ -280,7 +296,7 @@
Name[] names = arguments(1, lambdaType);
names[names.length - 1] = new Name(ValueConversions.identity(), names[1]);
LambdaForm form = new LambdaForm("identity", lambdaType.parameterCount(), names);
- return new SimpleMethodHandle(MethodType.methodType(refType, refType), form);
+ return SimpleMethodHandle.make(MethodType.methodType(refType, refType), form);
}
static MethodHandle makeVarargsCollector(MethodHandle target, Class<?> arrayType) {
@@ -334,8 +350,9 @@
MethodHandle collector;
try {
collector = asFixedArity().asCollector(arrayType, arrayLength);
+ assert(collector.type().parameterCount() == newArity) : "newArity="+newArity+" but collector="+collector;
} catch (IllegalArgumentException ex) {
- throw new WrongMethodTypeException("cannot build collector");
+ throw new WrongMethodTypeException("cannot build collector", ex);
}
cache = collector;
return collector.asType(newType);
@@ -429,12 +446,18 @@
names[names.length - 1] = new Name(target, (Object[]) targetArgs);
LambdaForm form = new LambdaForm("spread", lambdaType.parameterCount(), names);
- return new SimpleMethodHandle(srcType, form);
+ return SimpleMethodHandle.make(srcType, form);
}
static void checkSpreadArgument(Object av, int n) {
+ // FIXME: regression test for bug 7141637 erroneously expects an NPE, and other tests may expect IAE
+ // but the actual exception raised by an arity mismatch should be WMTE
+ final boolean RAISE_RANDOM_EXCEPTIONS = true; // FIXME: delete in JSR 292 M1
if (av == null) {
if (n == 0) return;
+ int len;
+ if (RAISE_RANDOM_EXCEPTIONS)
+ len = ((Object[])av).length; // throw NPE; but delete this after tests are fixed
} else if (av instanceof Object[]) {
int len = ((Object[])av).length;
if (len == n) return;
@@ -443,7 +466,9 @@
if (len == n) return;
}
// fall through to error:
- throw newIllegalArgumentException("Array is not of length "+n);
+ if (RAISE_RANDOM_EXCEPTIONS)
+ throw newIllegalArgumentException("Array is not of length "+n);
+ throw new WrongMethodTypeException("Array is not of length "+n);
}
private static final NamedFunction NF_checkSpreadArgument;
@@ -508,7 +533,7 @@
names[targetNamePos] = new Name(target, (Object[]) targetArgs);
LambdaForm form = new LambdaForm("collect", lambdaType.parameterCount(), names);
- return new SimpleMethodHandle(srcType, form);
+ return SimpleMethodHandle.make(srcType, form);
}
static
@@ -555,7 +580,7 @@
names[arity + 3] = new Name(new NamedFunction(invokeBasic), targetArgs);
LambdaForm form = new LambdaForm("guard", lambdaType.parameterCount(), names);
- return new SimpleMethodHandle(target.type(), form);
+ return SimpleMethodHandle.make(target.type(), form);
}
private static class GuardWithCatch {
diff --git a/jdk/src/share/classes/java/lang/invoke/MethodHandleNatives.java b/jdk/src/share/classes/java/lang/invoke/MethodHandleNatives.java
index a01f8a0..d27256fb 100644
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandleNatives.java
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandleNatives.java
@@ -325,15 +325,28 @@
static MemberName linkMethodImpl(Class<?> callerClass, int refKind,
Class<?> defc, String name, Object type,
Object[] appendixResult) {
- if (defc != MethodHandle.class || refKind != REF_invokeVirtual)
- throw new LinkageError("no such method "+defc.getName()+"."+name+type);
- switch (name) {
- case "invoke":
- return Invokers.genericInvokerMethod(callerClass, type, appendixResult);
- case "invokeExact":
- return Invokers.exactInvokerMethod(callerClass, type, appendixResult);
+ try {
+ if (defc == MethodHandle.class && refKind == REF_invokeVirtual) {
+ switch (name) {
+ case "invoke":
+ return Invokers.genericInvokerMethod(fixMethodType(callerClass, type), appendixResult);
+ case "invokeExact":
+ return Invokers.exactInvokerMethod(fixMethodType(callerClass, type), appendixResult);
+ }
+ }
+ } catch (Throwable ex) {
+ if (ex instanceof LinkageError)
+ throw (LinkageError) ex;
+ else
+ throw new LinkageError(ex.getMessage(), ex);
}
- throw new UnsupportedOperationException("linkMethod "+name);
+ throw new LinkageError("no such method "+defc.getName()+"."+name+type);
+ }
+ private static MethodType fixMethodType(Class<?> callerClass, Object type) {
+ if (type instanceof MethodType)
+ return (MethodType) type;
+ else
+ return MethodType.fromMethodDescriptorString((String)type, callerClass.getClassLoader());
}
// Tracing logic:
static MemberName linkMethodTracing(Class<?> callerClass, int refKind,
@@ -351,6 +364,7 @@
}
}
+
/**
* The JVM is resolving a CONSTANT_MethodHandle CP entry. And it wants our help.
* It will make an up-call to this method. (Do not change the name or signature.)
diff --git a/jdk/src/share/classes/java/lang/invoke/MethodHandles.java b/jdk/src/share/classes/java/lang/invoke/MethodHandles.java
index e338e96..5c55f20 100644
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandles.java
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandles.java
@@ -1876,6 +1876,17 @@
return MethodHandleImpl.makeCollectArguments(target, filter, pos, false);
}
+ // FIXME: Make this public in M1.
+ /*non-public*/ static
+ MethodHandle collectArguments(MethodHandle target, int pos, MethodHandle collector) {
+ MethodType targetType = target.type();
+ MethodType filterType = collector.type();
+ if (filterType.returnType() != void.class &&
+ filterType.returnType() != targetType.parameterType(pos))
+ throw newIllegalArgumentException("target and filter types do not match", targetType, filterType);
+ return MethodHandleImpl.makeCollectArguments(target, collector, pos, false);
+ }
+
/**
* Adapts a target method handle by post-processing
* its return value (if any) with a filter (another method handle).
diff --git a/jdk/src/share/classes/java/lang/invoke/MethodType.java b/jdk/src/share/classes/java/lang/invoke/MethodType.java
index 38610d8..c690dad 100644
--- a/jdk/src/share/classes/java/lang/invoke/MethodType.java
+++ b/jdk/src/share/classes/java/lang/invoke/MethodType.java
@@ -111,6 +111,36 @@
void setForm(MethodTypeForm f) { form = f; }
+ /** This number, mandated by the JVM spec as 255,
+ * is the maximum number of <em>slots</em>
+ * that any Java method can receive in its argument list.
+ * It limits both JVM signatures and method type objects.
+ * The longest possible invocation will look like
+ * {@code staticMethod(arg1, arg2, ..., arg255)} or
+ * {@code x.virtualMethod(arg1, arg2, ..., arg254)}.
+ */
+ /*non-public*/ static final int MAX_JVM_ARITY = 255; // this is mandated by the JVM spec.
+
+ /** This number is the maximum arity of a method handle, 254.
+ * It is derived from the absolute JVM-imposed arity by subtracting one,
+ * which is the slot occupied by the method handle itself at the
+ * beginning of the argument list used to invoke the method handle.
+ * The longest possible invocation will look like
+ * {@code mh.invoke(arg1, arg2, ..., arg254)}.
+ */
+ // Issue: Should we allow MH.invokeWithArguments to go to the full 255?
+ /*non-public*/ static final int MAX_MH_ARITY = MAX_JVM_ARITY-1; // deduct one for mh receiver
+
+ /** This number is the maximum arity of a method handle invoker, 253.
+ * It is derived from the absolute JVM-imposed arity by subtracting two,
+ * which are the slots occupied by invoke method handle, and the the
+ * target method handle, which are both at the beginning of the argument
+ * list used to invoke the target method handle.
+ * The longest possible invocation will look like
+ * {@code invokermh.invoke(targetmh, arg1, arg2, ..., arg253)}.
+ */
+ /*non-public*/ static final int MAX_MH_INVOKER_ARITY = MAX_MH_ARITY-1; // deduct one more for invoker
+
private static void checkRtype(Class<?> rtype) {
rtype.equals(rtype); // null check
}
@@ -131,7 +161,9 @@
return slots;
}
static void checkSlotCount(int count) {
- if ((count & 0xFF) != count)
+ assert((MAX_JVM_ARITY & (MAX_JVM_ARITY+1)) == 0);
+ // MAX_JVM_ARITY must be power of 2 minus 1 for following code trick to work:
+ if ((count & MAX_JVM_ARITY) != count)
throw newIllegalArgumentException("bad parameter count "+count);
}
private static IndexOutOfBoundsException newIndexOutOfBoundsException(Object num) {
diff --git a/jdk/src/share/classes/java/lang/invoke/SimpleMethodHandle.java b/jdk/src/share/classes/java/lang/invoke/SimpleMethodHandle.java
index 0ea6d38..1cd6daf 100644
--- a/jdk/src/share/classes/java/lang/invoke/SimpleMethodHandle.java
+++ b/jdk/src/share/classes/java/lang/invoke/SimpleMethodHandle.java
@@ -35,10 +35,14 @@
* @author jrose
*/
final class SimpleMethodHandle extends MethodHandle {
- SimpleMethodHandle(MethodType type, LambdaForm form) {
+ private SimpleMethodHandle(MethodType type, LambdaForm form) {
super(type, form);
}
+ /*non-public*/ static SimpleMethodHandle make(MethodType type, LambdaForm form) {
+ return new SimpleMethodHandle(type, form);
+ }
+
@Override
MethodHandle bindArgument(int pos, char basicType, Object value) {
MethodType type2 = type().dropParameterTypes(pos, pos+1);
diff --git a/jdk/src/share/classes/java/lang/invoke/WrongMethodTypeException.java b/jdk/src/share/classes/java/lang/invoke/WrongMethodTypeException.java
index 7d538dc..dba07d9 100644
--- a/jdk/src/share/classes/java/lang/invoke/WrongMethodTypeException.java
+++ b/jdk/src/share/classes/java/lang/invoke/WrongMethodTypeException.java
@@ -59,4 +59,27 @@
public WrongMethodTypeException(String s) {
super(s);
}
+
+ /**
+ * Constructs a {@code WrongMethodTypeException} with the specified
+ * detail message and cause.
+ *
+ * @param s the detail message.
+ * @param cause the cause of the exception, or null.
+ */
+ //FIXME: make this public in MR1
+ /*non-public*/ WrongMethodTypeException(String s, Throwable cause) {
+ super(s, cause);
+ }
+
+ /**
+ * Constructs a {@code WrongMethodTypeException} with the specified
+ * cause.
+ *
+ * @param cause the cause of the exception, or null.
+ */
+ //FIXME: make this public in MR1
+ /*non-public*/ WrongMethodTypeException(Throwable cause) {
+ super(cause);
+ }
}
diff --git a/jdk/src/share/classes/sun/invoke/util/ValueConversions.java b/jdk/src/share/classes/sun/invoke/util/ValueConversions.java
index a5e200f..1533f4b 100644
--- a/jdk/src/share/classes/sun/invoke/util/ValueConversions.java
+++ b/jdk/src/share/classes/sun/invoke/util/ValueConversions.java
@@ -44,6 +44,7 @@
static {
final Object[] values = { 255 };
AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ @Override
public Void run() {
values[0] = Integer.getInteger(THIS_CLASS.getName()+".MAX_ARITY", 255);
return null;
@@ -182,7 +183,7 @@
*/
public static Number primitiveConversion(Wrapper wrap, Object x, boolean cast) {
// Maybe merge this code with Wrapper.convert/cast.
- Number res = null;
+ Number res;
if (x == null) {
if (!cast) return null;
return ZERO_INT;
@@ -322,11 +323,9 @@
static void ignore(Object x) {
// no value to return; this is an unbox of null
- return;
}
static void empty() {
- return;
}
static Object zeroObject() {
@@ -390,24 +389,6 @@
/// Converting references to references.
/**
- * Value-killing function.
- * @param x an arbitrary reference value
- * @return a null
- */
- static Object alwaysNull(Object x) {
- return null;
- }
-
- /**
- * Value-killing function.
- * @param x an arbitrary reference value
- * @return a zero
- */
- static int alwaysZero(Object x) {
- return 0;
- }
-
- /**
* Identity function.
* @param x an arbitrary reference value
* @return the same value x
@@ -416,6 +397,10 @@
return x;
}
+ static <T> T[] identity(T[] x) {
+ return x;
+ }
+
/**
* Identity function on ints.
* @param x an arbitrary int value
@@ -468,29 +453,33 @@
return t.cast(x);
}
- private static final MethodHandle IDENTITY, CAST_REFERENCE, ALWAYS_NULL, ALWAYS_ZERO, ZERO_OBJECT, IGNORE, EMPTY, NEW_ARRAY;
+ private static final MethodHandle IDENTITY, CAST_REFERENCE, ZERO_OBJECT, IGNORE, EMPTY,
+ ARRAY_IDENTITY, FILL_NEW_TYPED_ARRAY, FILL_NEW_ARRAY;
static {
try {
MethodType idType = MethodType.genericMethodType(1);
MethodType castType = idType.insertParameterTypes(0, Class.class);
- MethodType alwaysZeroType = idType.changeReturnType(int.class);
MethodType ignoreType = idType.changeReturnType(void.class);
MethodType zeroObjectType = MethodType.genericMethodType(0);
IDENTITY = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", idType);
//CAST_REFERENCE = IMPL_LOOKUP.findVirtual(Class.class, "cast", idType);
CAST_REFERENCE = IMPL_LOOKUP.findStatic(THIS_CLASS, "castReference", castType);
- ALWAYS_NULL = IMPL_LOOKUP.findStatic(THIS_CLASS, "alwaysNull", idType);
- ALWAYS_ZERO = IMPL_LOOKUP.findStatic(THIS_CLASS, "alwaysZero", alwaysZeroType);
ZERO_OBJECT = IMPL_LOOKUP.findStatic(THIS_CLASS, "zeroObject", zeroObjectType);
IGNORE = IMPL_LOOKUP.findStatic(THIS_CLASS, "ignore", ignoreType);
EMPTY = IMPL_LOOKUP.findStatic(THIS_CLASS, "empty", ignoreType.dropParameterTypes(0, 1));
- NEW_ARRAY = IMPL_LOOKUP.findStatic(THIS_CLASS, "newArray", MethodType.methodType(Object[].class, int.class));
+ ARRAY_IDENTITY = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", MethodType.methodType(Object[].class, Object[].class));
+ FILL_NEW_ARRAY = IMPL_LOOKUP
+ .findStatic(THIS_CLASS, "fillNewArray",
+ MethodType.methodType(Object[].class, Integer.class, Object[].class));
+ FILL_NEW_TYPED_ARRAY = IMPL_LOOKUP
+ .findStatic(THIS_CLASS, "fillNewTypedArray",
+ MethodType.methodType(Object[].class, Object[].class, Integer.class, Object[].class));
} catch (NoSuchMethodException | IllegalAccessException ex) {
throw new InternalError("uncaught exception", ex);
}
}
- // Varargs methods need to be in a separately initialized class, to bootstrapping problems.
+ // Varargs methods need to be in a separately initialized class, to avoid bootstrapping problems.
static class LazyStatics {
private static final MethodHandle COPY_AS_REFERENCE_ARRAY, COPY_AS_PRIMITIVE_ARRAY, MAKE_LIST;
static {
@@ -505,34 +494,64 @@
}
}
+ static MethodHandle collectArguments(MethodHandle mh, int pos, MethodHandle collector) {
+ // FIXME: API needs public MHs.collectArguments.
+ // Should be:
+ // return MethodHandles.collectArguments(mh, 0, collector);
+ // The rest of this code is a workaround for not having that API.
+ if (COLLECT_ARGUMENTS != null) {
+ try {
+ return (MethodHandle)
+ COLLECT_ARGUMENTS.invokeExact(mh, pos, collector);
+ } catch (Throwable ex) {
+ if (ex instanceof RuntimeException)
+ throw (RuntimeException) ex;
+ if (ex instanceof Error)
+ throw (Error) ex;
+ throw new Error(ex.getMessage(), ex);
+ }
+ }
+ // Emulate MHs.collectArguments using fold + drop.
+ // This is slightly inefficient.
+ // More seriously, it can put a MH over the 255-argument limit.
+ mh = MethodHandles.dropArguments(mh, 1, collector.type().parameterList());
+ mh = MethodHandles.foldArguments(mh, collector);
+ return mh;
+ }
+ private static final MethodHandle COLLECT_ARGUMENTS;
+ static {
+ MethodHandle mh = null;
+ try {
+ java.lang.reflect.Method m = MethodHandles.class
+ .getDeclaredMethod("collectArguments",
+ MethodHandle.class, int.class, MethodHandle.class);
+ m.setAccessible(true);
+ mh = IMPL_LOOKUP.unreflect(m);
+
+ } catch (ReflectiveOperationException | SecurityException ex) {
+ throw new InternalError(ex);
+ }
+ COLLECT_ARGUMENTS = mh;
+ }
+
private static final EnumMap<Wrapper, MethodHandle>[] WRAPPER_CASTS
- = newWrapperCaches(2);
+ = newWrapperCaches(1);
/** Return a method that casts its sole argument (an Object) to the given type
- * and returns it as the given type (if exact is true), or as plain Object (if erase is true).
+ * and returns it as the given type.
*/
public static MethodHandle cast(Class<?> type) {
- boolean exact = false;
if (type.isPrimitive()) throw new IllegalArgumentException("cannot cast primitive type "+type);
- MethodHandle mh = null;
+ MethodHandle mh;
Wrapper wrap = null;
EnumMap<Wrapper, MethodHandle> cache = null;
if (Wrapper.isWrapperType(type)) {
wrap = Wrapper.forWrapperType(type);
- cache = WRAPPER_CASTS[exact?1:0];
+ cache = WRAPPER_CASTS[0];
mh = cache.get(wrap);
if (mh != null) return mh;
}
- if (VerifyType.isNullReferenceConversion(Object.class, type))
- mh = IDENTITY;
- else if (VerifyType.isNullType(type))
- mh = ALWAYS_NULL;
- else
- mh = MethodHandles.insertArguments(CAST_REFERENCE, 0, type);
- if (exact) {
- MethodType xmt = MethodType.methodType(type, Object.class);
- mh = MethodHandles.explicitCastArguments(mh, xmt);
- }
+ mh = MethodHandles.insertArguments(CAST_REFERENCE, 0, type);
if (cache != null)
cache.put(wrap, mh);
return mh;
@@ -920,37 +939,47 @@
}
private static final MethodHandle[] ARRAYS = makeArrays();
- // mh-fill versions of the above:
- private static Object[] newArray(int len) { return new Object[len]; }
+ // filling versions of the above:
+ // using Integer len instead of int len and no varargs to avoid bootstrapping problems
+ private static Object[] fillNewArray(Integer len, Object[] /*not ...*/ args) {
+ Object[] a = new Object[len];
+ fillWithArguments(a, 0, args);
+ return a;
+ }
+ private static Object[] fillNewTypedArray(Object[] example, Integer len, Object[] /*not ...*/ args) {
+ Object[] a = Arrays.copyOf(example, len);
+ fillWithArguments(a, 0, args);
+ return a;
+ }
private static void fillWithArguments(Object[] a, int pos, Object... args) {
System.arraycopy(args, 0, a, pos, args.length);
}
// using Integer pos instead of int pos to avoid bootstrapping problems
- private static Object[] fillArray(Object[] a, Integer pos, Object a0)
+ private static Object[] fillArray(Integer pos, Object[] a, Object a0)
{ fillWithArguments(a, pos, a0); return a; }
- private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1)
+ private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1)
{ fillWithArguments(a, pos, a0, a1); return a; }
- private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2)
+ private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2)
{ fillWithArguments(a, pos, a0, a1, a2); return a; }
- private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3)
+ private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3)
{ fillWithArguments(a, pos, a0, a1, a2, a3); return a; }
- private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3,
+ private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3,
Object a4)
{ fillWithArguments(a, pos, a0, a1, a2, a3, a4); return a; }
- private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3,
+ private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3,
Object a4, Object a5)
{ fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5); return a; }
- private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3,
+ private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3,
Object a4, Object a5, Object a6)
{ fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6); return a; }
- private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3,
+ private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3,
Object a4, Object a5, Object a6, Object a7)
{ fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7); return a; }
- private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3,
+ private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3,
Object a4, Object a5, Object a6, Object a7,
Object a8)
{ fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7, a8); return a; }
- private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3,
+ private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3,
Object a4, Object a5, Object a6, Object a7,
Object a8, Object a9)
{ fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); return a; }
@@ -958,7 +987,7 @@
ArrayList<MethodHandle> mhs = new ArrayList<>();
mhs.add(null); // there is no empty fill; at least a0 is required
for (;;) {
- MethodHandle mh = findCollector("fillArray", mhs.size(), Object[].class, Object[].class, Integer.class);
+ MethodHandle mh = findCollector("fillArray", mhs.size(), Object[].class, Integer.class, Object[].class);
if (mh == null) break;
mhs.add(mh);
}
@@ -984,69 +1013,95 @@
if (mh != null) return mh;
mh = findCollector("array", nargs, Object[].class);
if (mh != null) return ARRAYS[nargs] = mh;
- MethodHandle producer = filler(0); // identity function produces result
- return ARRAYS[nargs] = buildVarargsArray(producer, nargs);
+ mh = buildVarargsArray(FILL_NEW_ARRAY, ARRAY_IDENTITY, nargs);
+ assert(assertCorrectArity(mh, nargs));
+ return ARRAYS[nargs] = mh;
}
- private static MethodHandle buildVarargsArray(MethodHandle producer, int nargs) {
+ private static boolean assertCorrectArity(MethodHandle mh, int arity) {
+ assert(mh.type().parameterCount() == arity) : "arity != "+arity+": "+mh;
+ return true;
+ }
+
+ private static MethodHandle buildVarargsArray(MethodHandle newArray, MethodHandle finisher, int nargs) {
// Build up the result mh as a sequence of fills like this:
- // producer(fill(fill(fill(newArray(23),0,x1..x10),10,x11..x20),20,x21..x23))
+ // finisher(fill(fill(newArrayWA(23,x1..x10),10,x11..x20),20,x21..x23))
// The various fill(_,10*I,___*[J]) are reusable.
- MethodHandle filler = filler(nargs);
- MethodHandle mh = producer;
- mh = MethodHandles.dropArguments(mh, 1, filler.type().parameterList());
- mh = MethodHandles.foldArguments(mh, filler);
- mh = MethodHandles.foldArguments(mh, buildNewArray(nargs));
+ int leftLen = Math.min(nargs, LEFT_ARGS); // absorb some arguments immediately
+ int rightLen = nargs - leftLen;
+ MethodHandle leftCollector = newArray.bindTo(nargs);
+ leftCollector = leftCollector.asCollector(Object[].class, leftLen);
+ MethodHandle mh = finisher;
+ if (rightLen > 0) {
+ MethodHandle rightFiller = fillToRight(LEFT_ARGS + rightLen);
+ if (mh == ARRAY_IDENTITY)
+ mh = rightFiller;
+ else
+ mh = collectArguments(mh, 0, rightFiller);
+ }
+ if (mh == ARRAY_IDENTITY)
+ mh = leftCollector;
+ else
+ mh = collectArguments(mh, 0, leftCollector);
return mh;
}
- private static MethodHandle buildNewArray(int nargs) {
- return MethodHandles.insertArguments(NEW_ARRAY, 0, nargs);
- }
-
- private static final MethodHandle[] FILLERS = new MethodHandle[MAX_ARITY+1];
- // filler(N).invoke(a, arg0..arg[N-1]) fills a[0]..a[N-1]
- private static MethodHandle filler(int nargs) {
- MethodHandle filler = FILLERS[nargs];
+ private static final int LEFT_ARGS = (FILL_ARRAYS.length - 1);
+ private static final MethodHandle[] FILL_ARRAY_TO_RIGHT = new MethodHandle[MAX_ARITY+1];
+ /** fill_array_to_right(N).invoke(a, argL..arg[N-1])
+ * fills a[L]..a[N-1] with corresponding arguments,
+ * and then returns a. The value L is a global constant (LEFT_ARGS).
+ */
+ private static MethodHandle fillToRight(int nargs) {
+ MethodHandle filler = FILL_ARRAY_TO_RIGHT[nargs];
if (filler != null) return filler;
- return FILLERS[nargs] = buildFiller(nargs);
+ filler = buildFiller(nargs);
+ assert(assertCorrectArity(filler, nargs - LEFT_ARGS + 1));
+ return FILL_ARRAY_TO_RIGHT[nargs] = filler;
}
private static MethodHandle buildFiller(int nargs) {
- if (nargs == 0)
- return MethodHandles.identity(Object[].class);
- final int CHUNK = (FILL_ARRAYS.length - 1);
+ if (nargs <= LEFT_ARGS)
+ return ARRAY_IDENTITY; // no args to fill; return the array unchanged
+ // we need room for both mh and a in mh.invoke(a, arg*[nargs])
+ final int CHUNK = LEFT_ARGS;
int rightLen = nargs % CHUNK;
- int leftLen = nargs - rightLen;
+ int midLen = nargs - rightLen;
if (rightLen == 0) {
- leftLen = nargs - (rightLen = CHUNK);
- if (FILLERS[leftLen] == null) {
+ midLen = nargs - (rightLen = CHUNK);
+ if (FILL_ARRAY_TO_RIGHT[midLen] == null) {
// build some precursors from left to right
- for (int j = 0; j < leftLen; j += CHUNK) filler(j);
+ for (int j = LEFT_ARGS % CHUNK; j < midLen; j += CHUNK)
+ if (j > LEFT_ARGS) fillToRight(j);
}
}
- MethodHandle leftFill = filler(leftLen); // recursive fill
- MethodHandle rightFill = FILL_ARRAYS[rightLen];
- rightFill = MethodHandles.insertArguments(rightFill, 1, leftLen); // [leftLen..nargs-1]
+ if (midLen < LEFT_ARGS) rightLen = nargs - (midLen = LEFT_ARGS);
+ assert(rightLen > 0);
+ MethodHandle midFill = fillToRight(midLen); // recursive fill
+ MethodHandle rightFill = FILL_ARRAYS[rightLen].bindTo(midLen); // [midLen..nargs-1]
+ assert(midFill.type().parameterCount() == 1 + midLen - LEFT_ARGS);
+ assert(rightFill.type().parameterCount() == 1 + rightLen);
- // Combine the two fills: right(left(newArray(nargs), x1..x20), x21..x23)
- MethodHandle mh = filler(0); // identity function produces result
- mh = MethodHandles.dropArguments(mh, 1, rightFill.type().parameterList());
- mh = MethodHandles.foldArguments(mh, rightFill);
- if (leftLen > 0) {
- mh = MethodHandles.dropArguments(mh, 1, leftFill.type().parameterList());
- mh = MethodHandles.foldArguments(mh, leftFill);
- }
- return mh;
+ // Combine the two fills:
+ // right(mid(a, x10..x19), x20..x23)
+ // The final product will look like this:
+ // right(mid(newArrayLeft(24, x0..x9), x10..x19), x20..x23)
+ if (midLen == LEFT_ARGS)
+ return rightFill;
+ else
+ return collectArguments(rightFill, 0, midFill);
}
// Type-polymorphic version of varargs maker.
private static final ClassValue<MethodHandle[]> TYPED_COLLECTORS
= new ClassValue<MethodHandle[]>() {
+ @Override
protected MethodHandle[] computeValue(Class<?> type) {
return new MethodHandle[256];
}
};
+ static final int MAX_JVM_ARITY = 255; // limit imposed by the JVM
+
/** Return a method handle that takes the indicated number of
* typed arguments and returns an array of them.
* The type argument is the array type.
@@ -1055,16 +1110,36 @@
Class<?> elemType = arrayType.getComponentType();
if (elemType == null) throw new IllegalArgumentException("not an array: "+arrayType);
// FIXME: Need more special casing and caching here.
+ if (nargs >= MAX_JVM_ARITY/2 - 1) {
+ int slots = nargs;
+ final int MAX_ARRAY_SLOTS = MAX_JVM_ARITY - 1; // 1 for receiver MH
+ if (arrayType == double[].class || arrayType == long[].class)
+ slots *= 2;
+ if (slots > MAX_ARRAY_SLOTS)
+ throw new IllegalArgumentException("too many arguments: "+arrayType.getSimpleName()+", length "+nargs);
+ }
if (elemType == Object.class)
return varargsArray(nargs);
// other cases: primitive arrays, subtypes of Object[]
MethodHandle cache[] = TYPED_COLLECTORS.get(elemType);
MethodHandle mh = nargs < cache.length ? cache[nargs] : null;
if (mh != null) return mh;
- MethodHandle producer = buildArrayProducer(arrayType);
- mh = buildVarargsArray(producer, nargs);
+ if (elemType.isPrimitive()) {
+ MethodHandle builder = FILL_NEW_ARRAY;
+ MethodHandle producer = buildArrayProducer(arrayType);
+ mh = buildVarargsArray(builder, producer, nargs);
+ } else {
+ @SuppressWarnings("unchecked")
+ Class<? extends Object[]> objArrayType = (Class<? extends Object[]>) arrayType;
+ Object[] example = Arrays.copyOf(NO_ARGS_ARRAY, 0, objArrayType);
+ MethodHandle builder = FILL_NEW_TYPED_ARRAY.bindTo(example);
+ MethodHandle producer = ARRAY_IDENTITY;
+ mh = buildVarargsArray(builder, producer, nargs);
+ }
mh = mh.asType(MethodType.methodType(arrayType, Collections.<Class<?>>nCopies(nargs, elemType)));
- cache[nargs] = mh;
+ assert(assertCorrectArity(mh, nargs));
+ if (nargs < cache.length)
+ cache[nargs] = mh;
return mh;
}