提交 9afe3461 编写于 作者: J jrose

7191102: nightly failures after JSR 292 lazy method handle update (round 3)

Reviewed-by: twisti, kvn
上级 5bf74720
...@@ -142,6 +142,11 @@ import com.sun.xml.internal.ws.org.objectweb.asm.Type; ...@@ -142,6 +142,11 @@ import com.sun.xml.internal.ws.org.objectweb.asm.Type;
*/ */
protected abstract SpeciesData speciesData(); protected abstract SpeciesData speciesData();
@Override
final Object internalProperties() {
return "/BMH="+internalValues();
}
@Override @Override
final Object internalValues() { final Object internalValues() {
Object[] boundValues = new Object[speciesData().fieldCount()]; Object[] boundValues = new Object[speciesData().fieldCount()];
......
...@@ -108,8 +108,8 @@ class DirectMethodHandle extends MethodHandle { ...@@ -108,8 +108,8 @@ class DirectMethodHandle extends MethodHandle {
} }
@Override @Override
String debugString() { String internalProperties() {
return "DMH["+member.toString()+"]="+super.debugString(); return "/DMH="+member.toString();
} }
//// Implementation methods. //// Implementation methods.
......
...@@ -185,12 +185,17 @@ class InvokerBytecodeGenerator { ...@@ -185,12 +185,17 @@ class InvokerBytecodeGenerator {
} }
class CpPatch { class CpPatch {
int index; final int index;
Object value; final String placeholder;
CpPatch(int index, Object value) { final Object value;
CpPatch(int index, String placeholder, Object value) {
this.index = index; this.index = index;
this.placeholder = placeholder;
this.value = value; this.value = value;
} }
public String toString() {
return "CpPatch/index="+index+",placeholder="+placeholder+",value="+value;
}
} }
Map<Object, CpPatch> cpPatches = new HashMap<>(); Map<Object, CpPatch> cpPatches = new HashMap<>();
...@@ -205,7 +210,7 @@ class InvokerBytecodeGenerator { ...@@ -205,7 +210,7 @@ class InvokerBytecodeGenerator {
} }
// insert placeholder in CP and remember the patch // insert placeholder in CP and remember the patch
int index = cw.newConst((Object) cpPlaceholder); // TODO check if aready in the constant pool 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; return cpPlaceholder;
} }
...@@ -213,7 +218,9 @@ class InvokerBytecodeGenerator { ...@@ -213,7 +218,9 @@ class InvokerBytecodeGenerator {
int size = getConstantPoolSize(classFile); int size = getConstantPoolSize(classFile);
Object[] res = new Object[size]; Object[] res = new Object[size];
for (CpPatch p : cpPatches.values()) { 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; return res;
} }
......
...@@ -74,8 +74,18 @@ class Invokers { ...@@ -74,8 +74,18 @@ class Invokers {
MethodHandle invoker = exactInvoker; MethodHandle invoker = exactInvoker;
if (invoker != null) return invoker; if (invoker != null) return invoker;
MethodType mtype = targetType; MethodType mtype = targetType;
LambdaForm lform = invokeForm(mtype, MethodTypeForm.LF_EX_INVOKER); MethodType invokerType = mtype.invokerType();
invoker = BoundMethodHandle.bindSingle(mtype.invokerType(), lform, mtype); 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)); assert(checkInvoker(invoker));
exactInvoker = invoker; exactInvoker = invoker;
return invoker; return invoker;
...@@ -85,9 +95,20 @@ class Invokers { ...@@ -85,9 +95,20 @@ class Invokers {
MethodHandle invoker = generalInvoker; MethodHandle invoker = generalInvoker;
if (invoker != null) return invoker; if (invoker != null) return invoker;
MethodType mtype = targetType; MethodType mtype = targetType;
prepareForGenericCall(mtype); MethodType invokerType = mtype.invokerType();
LambdaForm lform = invokeForm(mtype, MethodTypeForm.LF_GEN_INVOKER); LambdaForm lform;
invoker = BoundMethodHandle.bindSingle(mtype.invokerType(), lform, mtype); 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)); assert(checkInvoker(invoker));
generalInvoker = invoker; generalInvoker = invoker;
return invoker; return invoker;
...@@ -102,6 +123,7 @@ class Invokers { ...@@ -102,6 +123,7 @@ class Invokers {
} }
static MemberName invokeBasicMethod(MethodType type) { static MemberName invokeBasicMethod(MethodType type) {
type = type.basicType();
String name = "invokeBasic"; String name = "invokeBasic";
try { try {
//Lookup.findVirtual(MethodHandle.class, name, type); //Lookup.findVirtual(MethodHandle.class, name, type);
...@@ -135,9 +157,31 @@ class Invokers { ...@@ -135,9 +157,31 @@ class Invokers {
/*non-public*/ MethodHandle spreadInvoker(int leadingArgCount) { /*non-public*/ MethodHandle spreadInvoker(int leadingArgCount) {
MethodHandle vaInvoker = spreadInvokers[leadingArgCount]; MethodHandle vaInvoker = spreadInvokers[leadingArgCount];
if (vaInvoker != null) return vaInvoker; if (vaInvoker != null) return vaInvoker;
MethodHandle gInvoker = generalInvoker();
int spreadArgCount = targetType.parameterCount() - leadingArgCount; 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; spreadInvokers[leadingArgCount] = vaInvoker;
return vaInvoker; return vaInvoker;
} }
...@@ -171,7 +215,7 @@ class Invokers { ...@@ -171,7 +215,7 @@ class Invokers {
.findStatic(CallSite.class, "uninitializedCallSite", .findStatic(CallSite.class, "uninitializedCallSite",
MethodType.methodType(Empty.class)); MethodType.methodType(Empty.class));
} catch (ReflectiveOperationException ex) { } catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex); throw new InternalError(ex);
} }
} }
invoker = MethodHandles.explicitCastArguments(invoker, MethodType.methodType(targetType.returnType())); invoker = MethodHandles.explicitCastArguments(invoker, MethodType.methodType(targetType.returnType()));
...@@ -185,30 +229,39 @@ class Invokers { ...@@ -185,30 +229,39 @@ class Invokers {
return "Invokers"+targetType; return "Invokers"+targetType;
} }
private static MethodType fixMethodType(Class<?> callerClass, Object type) { static MemberName exactInvokerMethod(MethodType mtype, Object[] appendixResult) {
if (type instanceof MethodType) LambdaForm lform;
return (MethodType) type; final int MTYPE_ARG_APPENDED = 1; // argument count for appended mtype value
else if (mtype.parameterSlotCount() <= MethodType.MAX_MH_ARITY - MTYPE_ARG_APPENDED) {
return MethodType.fromMethodDescriptorString((String)type, callerClass.getClassLoader()); lform = invokeForm(mtype, false, MethodTypeForm.LF_EX_LINKER);
} appendixResult[0] = mtype;
} else {
static MemberName exactInvokerMethod(Class<?> callerClass, Object type, Object[] appendixResult) { lform = invokeForm(mtype, true, MethodTypeForm.LF_EX_LINKER);
MethodType mtype = fixMethodType(callerClass, type); }
LambdaForm lform = invokeForm(mtype, MethodTypeForm.LF_EX_LINKER);
appendixResult[0] = mtype;
return lform.vmentry; return lform.vmentry;
} }
static MemberName genericInvokerMethod(Class<?> callerClass, Object type, Object[] appendixResult) { static MemberName genericInvokerMethod(MethodType mtype, Object[] appendixResult) {
MethodType mtype = fixMethodType(callerClass, type); LambdaForm lform;
LambdaForm lform = invokeForm(mtype, MethodTypeForm.LF_GEN_LINKER); final int MTYPE_ARG_APPENDED = 1; // argument count for appended mtype value
prepareForGenericCall(mtype); if (mtype.parameterSlotCount() <= MethodType.MAX_MH_ARITY - (MTYPE_ARG_APPENDED + GENERIC_INVOKER_SLOP)) {
appendixResult[0] = mtype; 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; return lform.vmentry;
} }
private static LambdaForm invokeForm(MethodType mtype, int which) { private static LambdaForm invokeForm(MethodType mtype, boolean customized, int which) {
mtype = mtype.basicType(); // normalize Z to I, String to Object, etc. 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; boolean isLinker, isGeneric;
String debugName; String debugName;
switch (which) { switch (which) {
...@@ -218,27 +271,32 @@ class Invokers { ...@@ -218,27 +271,32 @@ class Invokers {
case MethodTypeForm.LF_GEN_INVOKER: isLinker = false; isGeneric = true; debugName = "invoker"; break; case MethodTypeForm.LF_GEN_INVOKER: isLinker = false; isGeneric = true; debugName = "invoker"; break;
default: throw new InternalError(); default: throw new InternalError();
} }
LambdaForm lform = mtype.form().cachedLambdaForm(which); LambdaForm lform;
if (lform != null) return lform; if (isCached) {
lform = mtype.form().cachedLambdaForm(which);
if (lform != null) return lform;
}
// exactInvokerForm (Object,Object)Object // exactInvokerForm (Object,Object)Object
// link with java.lang.invoke.MethodHandle.invokeBasic(MethodHandle,Object,Object)Object/invokeSpecial // link with java.lang.invoke.MethodHandle.invokeBasic(MethodHandle,Object,Object)Object/invokeSpecial
final int THIS_MH = 0; final int THIS_MH = 0;
final int CALL_MH = THIS_MH + (isLinker ? 0 : 1); final int CALL_MH = THIS_MH + (isLinker ? 0 : 1);
final int ARG_BASE = CALL_MH + 1; final int ARG_BASE = CALL_MH + 1;
final int OUTARG_LIMIT = ARG_BASE + mtype.parameterCount(); 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; 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 CHECK_TYPE = nameCursor++;
final int LINKER_CALL = nameCursor++; final int LINKER_CALL = nameCursor++;
MethodType invokerFormType = mtype.invokerType(); MethodType invokerFormType = mtype.invokerType();
if (isLinker) { if (isLinker) {
invokerFormType = invokerFormType.appendParameterTypes(MemberName.class); if (!customized)
invokerFormType = invokerFormType.appendParameterTypes(MemberName.class);
} else { } else {
invokerFormType = invokerFormType.invokerType(); invokerFormType = invokerFormType.invokerType();
} }
Name[] names = arguments(nameCursor - INARG_LIMIT, invokerFormType); 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) { if (MTYPE_ARG >= INARG_LIMIT) {
assert(names[MTYPE_ARG] == null); assert(names[MTYPE_ARG] == null);
names[MTYPE_ARG] = BoundMethodHandle.getSpeciesData("L").getterName(names[THIS_MH], 0); names[MTYPE_ARG] = BoundMethodHandle.getSpeciesData("L").getterName(names[THIS_MH], 0);
...@@ -248,31 +306,42 @@ class Invokers { ...@@ -248,31 +306,42 @@ class Invokers {
// Make the final call. If isGeneric, then prepend the result of type checking. // Make the final call. If isGeneric, then prepend the result of type checking.
MethodType outCallType; MethodType outCallType;
Object[] outArgs; Object[] outArgs;
Object mtypeArg = (customized ? mtype : names[MTYPE_ARG]);
if (!isGeneric) { 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*) // mh.invokeExact(a*):R => checkExactType(mh, TYPEOF(a*:R)); mh.invokeBasic(a*)
outArgs = Arrays.copyOfRange(names, CALL_MH, OUTARG_LIMIT, Object[].class); outArgs = Arrays.copyOfRange(names, CALL_MH, OUTARG_LIMIT, Object[].class);
outCallType = mtype; 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 { } 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 => // mh.invokeGeneric(a*):R =>
// let mt=TYPEOF(a*:R), gamh=checkGenericType(mh, mt); // let mt=TYPEOF(a*:R), gamh=checkGenericType(mh, mt);
// gamh.invokeBasic(mt, mh, a*) // gamh.invokeBasic(mt, mh, a*)
final int PREPEND_GAMH = 0, PREPEND_MT = 1, PREPEND_COUNT = 2; 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); outArgs = Arrays.copyOfRange(names, CALL_MH, OUTARG_LIMIT + PREPEND_COUNT, Object[].class);
// prepend arguments: // prepend arguments:
System.arraycopy(outArgs, 0, outArgs, PREPEND_COUNT, outArgs.length - PREPEND_COUNT); System.arraycopy(outArgs, 0, outArgs, PREPEND_COUNT, outArgs.length - PREPEND_COUNT);
outArgs[PREPEND_GAMH] = names[CHECK_TYPE]; outArgs[PREPEND_GAMH] = names[CHECK_TYPE];
outArgs[PREPEND_MT] = names[MTYPE_ARG]; outArgs[PREPEND_MT] = mtypeArg;
outCallType = mtype.insertParameterTypes(0, MethodType.class, MethodHandle.class); outCallType = mtype.insertParameterTypes(0, MethodType.class, MethodHandle.class);
} }
names[LINKER_CALL] = new Name(invokeBasicMethod(outCallType), outArgs); names[LINKER_CALL] = new Name(invokeBasicMethod(outCallType), outArgs);
lform = new LambdaForm(debugName, INARG_LIMIT, names); lform = new LambdaForm(debugName, INARG_LIMIT, names);
if (isLinker) if (isLinker)
lform.compileToBytecode(); // JVM needs a real methodOop lform.compileToBytecode(); // JVM needs a real methodOop
lform = mtype.form().setCachedLambdaForm(which, lform); if (isCached)
lform = mtype.form().setCachedLambdaForm(which, lform);
return lform; return lform;
} }
private static final int GENERIC_INVOKER_SLOP = 2; // used elsewhere to avoid arity problems
/*non-public*/ static /*non-public*/ static
WrongMethodTypeException newWrongMethodTypeException(MethodType actual, MethodType expected) { WrongMethodTypeException newWrongMethodTypeException(MethodType actual, MethodType expected) {
...@@ -370,6 +439,7 @@ class Invokers { ...@@ -370,6 +439,7 @@ class Invokers {
// Local constant functions: // Local constant functions:
private static final NamedFunction NF_checkExactType; private static final NamedFunction NF_checkExactType;
private static final NamedFunction NF_checkGenericType; private static final NamedFunction NF_checkGenericType;
private static final NamedFunction NF_asType;
private static final NamedFunction NF_getCallSiteTarget; private static final NamedFunction NF_getCallSiteTarget;
static { static {
try { try {
...@@ -377,6 +447,8 @@ class Invokers { ...@@ -377,6 +447,8 @@ class Invokers {
.getDeclaredMethod("checkExactType", Object.class, Object.class)); .getDeclaredMethod("checkExactType", Object.class, Object.class));
NF_checkGenericType = new NamedFunction(Invokers.class NF_checkGenericType = new NamedFunction(Invokers.class
.getDeclaredMethod("checkGenericType", Object.class, Object.class)); .getDeclaredMethod("checkGenericType", Object.class, Object.class));
NF_asType = new NamedFunction(MethodHandle.class
.getDeclaredMethod("asType", MethodType.class));
NF_getCallSiteTarget = new NamedFunction(Invokers.class NF_getCallSiteTarget = new NamedFunction(Invokers.class
.getDeclaredMethod("getCallSiteTarget", Object.class)); .getDeclaredMethod("getCallSiteTarget", Object.class));
NF_checkExactType.resolve(); NF_checkExactType.resolve();
......
...@@ -596,14 +596,7 @@ class LambdaForm { ...@@ -596,14 +596,7 @@ class LambdaForm {
Object interpretWithArguments(Object... argumentValues) throws Throwable { Object interpretWithArguments(Object... argumentValues) throws Throwable {
if (TRACE_INTERPRETER) if (TRACE_INTERPRETER)
return interpretWithArgumentsTracing(argumentValues); return interpretWithArgumentsTracing(argumentValues);
if (COMPILE_THRESHOLD != 0 && checkInvocationCounter();
invocationCounter < COMPILE_THRESHOLD) {
invocationCounter++; // benign race
if (invocationCounter >= COMPILE_THRESHOLD) {
// Replace vmentry with a bytecode version of this LF.
compileToBytecode();
}
}
assert(arityCheck(argumentValues)); assert(arityCheck(argumentValues));
Object[] values = Arrays.copyOf(argumentValues, names.length); Object[] values = Arrays.copyOf(argumentValues, names.length);
for (int i = argumentValues.length; i < values.length; i++) { for (int i = argumentValues.length; i < values.length; i++) {
...@@ -630,6 +623,16 @@ class LambdaForm { ...@@ -630,6 +623,16 @@ class LambdaForm {
return name.function.invokeWithArguments(arguments); 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 { Object interpretWithArgumentsTracing(Object... argumentValues) throws Throwable {
traceInterpreter("[ interpretWithArguments", this, argumentValues); traceInterpreter("[ interpretWithArguments", this, argumentValues);
if (invocationCounter < COMPILE_THRESHOLD) { if (invocationCounter < COMPILE_THRESHOLD) {
...@@ -703,7 +706,7 @@ class LambdaForm { ...@@ -703,7 +706,7 @@ class LambdaForm {
} }
public String toString() { public String toString() {
StringBuilder buf = new StringBuilder("Lambda("); StringBuilder buf = new StringBuilder(debugName+"=Lambda(");
for (int i = 0; i < names.length; i++) { for (int i = 0; i < names.length; i++) {
if (i == arity) buf.append(")=>{"); if (i == arity) buf.append(")=>{");
Name n = names[i]; Name n = names[i];
......
...@@ -924,7 +924,7 @@ assertEquals("[123]", (String) longsToString.invokeExact((long)123)); ...@@ -924,7 +924,7 @@ assertEquals("[123]", (String) longsToString.invokeExact((long)123));
if (arrayType != type().parameterType(collectArgPos)) if (arrayType != type().parameterType(collectArgPos))
target = convertArguments(type().changeParameterType(collectArgPos, arrayType)); target = convertArguments(type().changeParameterType(collectArgPos, arrayType));
MethodHandle collector = ValueConversions.varargsArray(arrayType, arrayLength); 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 // private API: return true if last param exactly matches arrayType
...@@ -1226,7 +1226,7 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString()); ...@@ -1226,7 +1226,7 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
return "MethodHandle"+type; return "MethodHandle"+type;
} }
String debugString() { String debugString() {
return standardString()+"="+internalForm()+internalValues(); return standardString()+"/LF="+internalForm()+internalProperties();
} }
//// Implementation methods. //// Implementation methods.
...@@ -1269,6 +1269,12 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString()); ...@@ -1269,6 +1269,12 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
/*non-public*/ /*non-public*/
Object internalValues() { Object internalValues() {
return null;
}
/*non-public*/
Object internalProperties() {
// Override to something like "/FOO=bar"
return ""; return "";
} }
......
...@@ -59,7 +59,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; ...@@ -59,7 +59,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
Name[] args = Arrays.copyOfRange(names, 1, 1 + srcType.parameterCount()); Name[] args = Arrays.copyOfRange(names, 1, 1 + srcType.parameterCount());
names[names.length - 1] = new Name(accessor.asType(srcType), (Object[]) args); names[names.length - 1] = new Name(accessor.asType(srcType), (Object[]) args);
LambdaForm form = new LambdaForm("getElement", lambdaType.parameterCount(), names); LambdaForm form = new LambdaForm("getElement", lambdaType.parameterCount(), names);
MethodHandle mh = new SimpleMethodHandle(srcType, form); MethodHandle mh = SimpleMethodHandle.make(srcType, form);
if (ArrayAccessor.needCast(arrayClass)) { if (ArrayAccessor.needCast(arrayClass)) {
mh = mh.bindTo(arrayClass); mh = mh.bindTo(arrayClass);
} }
...@@ -171,38 +171,46 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; ...@@ -171,38 +171,46 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
// Calculate extra arguments (temporaries) required in the names array. // Calculate extra arguments (temporaries) required in the names array.
// FIXME: Use an ArrayList<Name>. Some arguments require more than one conversion step. // FIXME: Use an ArrayList<Name>. Some arguments require more than one conversion step.
int extra = 0; final int INARG_COUNT = srcType.parameterCount();
for (int i = 0; i < srcType.parameterCount(); i++) { int conversions = 0;
Class<?> src = srcType.parameterType(i); boolean[] needConv = new boolean[1+INARG_COUNT];
Class<?> dst = dstType.parameterType(i); for (int i = 0; i <= INARG_COUNT; i++) {
if (!VerifyType.isNullConversion(src, dst)) { Class<?> src = (i == INARG_COUNT) ? dstType.returnType() : srcType.parameterType(i);
extra++; 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(); final int IN_MH = 0;
Class<?> haveReturn = dstType.returnType(); final int INARG_BASE = 1;
boolean retConv = !VerifyType.isNullConversion(haveReturn, needReturn); 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. // Now build a LambdaForm.
MethodType lambdaType = srcType.invokerType(); MethodType lambdaType = srcType.basicType().invokerType();
Name[] names = arguments(extra + 1, lambdaType); Name[] names = arguments(NAME_LIMIT - INARG_LIMIT, lambdaType);
int[] indexes = new int[lambdaType.parameterCount()];
// 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];
MethodType midType = dstType; int nameCursor = INARG_LIMIT;
for (int i = 0, argIndex = 1, tmpIndex = lambdaType.parameterCount(); i < srcType.parameterCount(); i++, argIndex++) { for (int i = 0; i < INARG_COUNT; i++) {
Class<?> src = srcType.parameterType(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 // do nothing: difference is trivial
indexes[i] = argIndex; outArgs[OUTARG_BASE + i] = names[INARG_BASE + i];
continue; continue;
} }
// Work the current type backward toward the desired caller type:
midType = midType.changeParameterType(i, src);
// Tricky case analysis follows. // Tricky case analysis follows.
MethodHandle fn = null; MethodHandle fn = null;
if (src.isPrimitive()) { if (src.isPrimitive()) {
...@@ -246,33 +254,41 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; ...@@ -246,33 +254,41 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
fn = ValueConversions.cast(dst); fn = ValueConversions.cast(dst);
} }
} }
names[tmpIndex] = new Name(fn, names[argIndex]); Name conv = new Name(fn, names[INARG_BASE + i]);
indexes[i] = tmpIndex; assert(names[nameCursor] == null);
tmpIndex++; names[nameCursor++] = conv;
assert(outArgs[OUTARG_BASE + i] == null);
outArgs[OUTARG_BASE + i] = conv;
} }
if (retConv) {
MethodHandle adjustReturn; // Build argument array for the call.
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) { if (haveReturn == void.class) {
// synthesize a zero value for the given void // synthesize a zero value for the given void
Object zero = Wrapper.forBasicType(needReturn).zero(); Object zero = Wrapper.forBasicType(needReturn).zero();
adjustReturn = MethodHandles.constant(needReturn, zero); fn = MethodHandles.constant(needReturn, zero);
arg = new Object[0]; // don't pass names[OUT_CALL] to conversion
} else { } else {
MethodHandle identity = MethodHandles.identity(needReturn); MethodHandle identity = MethodHandles.identity(needReturn);
MethodType needConversion = identity.type().changeParameterType(0, haveReturn); MethodType needConversion = identity.type().changeParameterType(0, haveReturn);
adjustReturn = makePairwiseConvert(identity, needConversion, level); fn = makePairwiseConvert(identity, needConversion, level);
} }
target = makeCollectArguments(adjustReturn, target, 0, false); assert(names[RETURN_CONV] == null);
names[RETURN_CONV] = new Name(fn, arg);
assert(RETURN_CONV == names.length-1);
} }
// 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];
}
names[names.length - 1] = new Name(target, (Object[]) targetArgs);
LambdaForm form = new LambdaForm("convert", lambdaType.parameterCount(), names); LambdaForm form = new LambdaForm("convert", lambdaType.parameterCount(), names);
return new SimpleMethodHandle(srcType, form); return SimpleMethodHandle.make(srcType, form);
} }
static MethodHandle makeReferenceIdentity(Class<?> refType) { static MethodHandle makeReferenceIdentity(Class<?> refType) {
...@@ -280,7 +296,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; ...@@ -280,7 +296,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
Name[] names = arguments(1, lambdaType); Name[] names = arguments(1, lambdaType);
names[names.length - 1] = new Name(ValueConversions.identity(), names[1]); names[names.length - 1] = new Name(ValueConversions.identity(), names[1]);
LambdaForm form = new LambdaForm("identity", lambdaType.parameterCount(), names); 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) { static MethodHandle makeVarargsCollector(MethodHandle target, Class<?> arrayType) {
...@@ -334,8 +350,9 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; ...@@ -334,8 +350,9 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
MethodHandle collector; MethodHandle collector;
try { try {
collector = asFixedArity().asCollector(arrayType, arrayLength); collector = asFixedArity().asCollector(arrayType, arrayLength);
assert(collector.type().parameterCount() == newArity) : "newArity="+newArity+" but collector="+collector;
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
throw new WrongMethodTypeException("cannot build collector"); throw new WrongMethodTypeException("cannot build collector", ex);
} }
cache = collector; cache = collector;
return collector.asType(newType); return collector.asType(newType);
...@@ -429,12 +446,18 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; ...@@ -429,12 +446,18 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
names[names.length - 1] = new Name(target, (Object[]) targetArgs); names[names.length - 1] = new Name(target, (Object[]) targetArgs);
LambdaForm form = new LambdaForm("spread", lambdaType.parameterCount(), names); 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) { 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 (av == null) {
if (n == 0) return; 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[]) { } else if (av instanceof Object[]) {
int len = ((Object[])av).length; int len = ((Object[])av).length;
if (len == n) return; if (len == n) return;
...@@ -443,7 +466,9 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; ...@@ -443,7 +466,9 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
if (len == n) return; if (len == n) return;
} }
// fall through to error: // 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; private static final NamedFunction NF_checkSpreadArgument;
...@@ -508,7 +533,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; ...@@ -508,7 +533,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
names[targetNamePos] = new Name(target, (Object[]) targetArgs); names[targetNamePos] = new Name(target, (Object[]) targetArgs);
LambdaForm form = new LambdaForm("collect", lambdaType.parameterCount(), names); LambdaForm form = new LambdaForm("collect", lambdaType.parameterCount(), names);
return new SimpleMethodHandle(srcType, form); return SimpleMethodHandle.make(srcType, form);
} }
static static
...@@ -555,7 +580,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; ...@@ -555,7 +580,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
names[arity + 3] = new Name(new NamedFunction(invokeBasic), targetArgs); names[arity + 3] = new Name(new NamedFunction(invokeBasic), targetArgs);
LambdaForm form = new LambdaForm("guard", lambdaType.parameterCount(), names); LambdaForm form = new LambdaForm("guard", lambdaType.parameterCount(), names);
return new SimpleMethodHandle(target.type(), form); return SimpleMethodHandle.make(target.type(), form);
} }
private static class GuardWithCatch { private static class GuardWithCatch {
......
...@@ -325,15 +325,28 @@ class MethodHandleNatives { ...@@ -325,15 +325,28 @@ class MethodHandleNatives {
static MemberName linkMethodImpl(Class<?> callerClass, int refKind, static MemberName linkMethodImpl(Class<?> callerClass, int refKind,
Class<?> defc, String name, Object type, Class<?> defc, String name, Object type,
Object[] appendixResult) { Object[] appendixResult) {
if (defc != MethodHandle.class || refKind != REF_invokeVirtual) try {
throw new LinkageError("no such method "+defc.getName()+"."+name+type); if (defc == MethodHandle.class && refKind == REF_invokeVirtual) {
switch (name) { switch (name) {
case "invoke": case "invoke":
return Invokers.genericInvokerMethod(callerClass, type, appendixResult); return Invokers.genericInvokerMethod(fixMethodType(callerClass, type), appendixResult);
case "invokeExact": case "invokeExact":
return Invokers.exactInvokerMethod(callerClass, type, appendixResult); 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: // Tracing logic:
static MemberName linkMethodTracing(Class<?> callerClass, int refKind, static MemberName linkMethodTracing(Class<?> callerClass, int refKind,
...@@ -351,6 +364,7 @@ class MethodHandleNatives { ...@@ -351,6 +364,7 @@ class MethodHandleNatives {
} }
} }
/** /**
* The JVM is resolving a CONSTANT_MethodHandle CP entry. And it wants our help. * 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.) * It will make an up-call to this method. (Do not change the name or signature.)
......
...@@ -1876,6 +1876,17 @@ assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY ...@@ -1876,6 +1876,17 @@ assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY
return MethodHandleImpl.makeCollectArguments(target, filter, pos, false); 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 * Adapts a target method handle by post-processing
* its return value (if any) with a filter (another method handle). * its return value (if any) with a filter (another method handle).
......
...@@ -111,6 +111,36 @@ class MethodType implements java.io.Serializable { ...@@ -111,6 +111,36 @@ class MethodType implements java.io.Serializable {
void setForm(MethodTypeForm f) { form = f; } 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) { private static void checkRtype(Class<?> rtype) {
rtype.equals(rtype); // null check rtype.equals(rtype); // null check
} }
...@@ -131,7 +161,9 @@ class MethodType implements java.io.Serializable { ...@@ -131,7 +161,9 @@ class MethodType implements java.io.Serializable {
return slots; return slots;
} }
static void checkSlotCount(int count) { 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); throw newIllegalArgumentException("bad parameter count "+count);
} }
private static IndexOutOfBoundsException newIndexOutOfBoundsException(Object num) { private static IndexOutOfBoundsException newIndexOutOfBoundsException(Object num) {
......
...@@ -35,10 +35,14 @@ import java.util.logging.Logger; ...@@ -35,10 +35,14 @@ import java.util.logging.Logger;
* @author jrose * @author jrose
*/ */
final class SimpleMethodHandle extends MethodHandle { final class SimpleMethodHandle extends MethodHandle {
SimpleMethodHandle(MethodType type, LambdaForm form) { private SimpleMethodHandle(MethodType type, LambdaForm form) {
super(type, form); super(type, form);
} }
/*non-public*/ static SimpleMethodHandle make(MethodType type, LambdaForm form) {
return new SimpleMethodHandle(type, form);
}
@Override @Override
MethodHandle bindArgument(int pos, char basicType, Object value) { MethodHandle bindArgument(int pos, char basicType, Object value) {
MethodType type2 = type().dropParameterTypes(pos, pos+1); MethodType type2 = type().dropParameterTypes(pos, pos+1);
......
...@@ -59,4 +59,27 @@ public class WrongMethodTypeException extends RuntimeException { ...@@ -59,4 +59,27 @@ public class WrongMethodTypeException extends RuntimeException {
public WrongMethodTypeException(String s) { public WrongMethodTypeException(String s) {
super(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);
}
} }
此差异已折叠。
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @summary BoundMethodHandle tests with primitive types
* @compile MaxTest.java
* @run junit/othervm test.java.lang.invoke.MaxTest
*/
package test.java.lang.invoke;
import static org.junit.Assert.assertEquals;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import org.junit.Test;
public class MaxTest {
static MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
private MethodHandle getMax(Class<?> t) throws Throwable {
return LOOKUP.findStatic(Math.class, "max", MethodType.methodType(t, t, t));
}
static int ITERATION_COUNT = 40000;
static {
String iterations = System.getProperty(MaxTest.class.getSimpleName() + ".ITERATION_COUNT");
if (iterations == null) {
iterations = System.getProperty(MaxTest.class.getName() + ".ITERATION_COUNT");
}
if (iterations != null) {
ITERATION_COUNT = Integer.parseInt(iterations);
}
}
@Test
public void testMaxLong() throws Throwable {
final Class<?> C = long.class;
final long P = 23L;
final long Q = 42L;
final long R = Math.max(P, Q);
for (int i = 0; i < ITERATION_COUNT; ++i) {
MethodHandle h = getMax(C);
assertEquals((long) h.invokeExact(P, Q), R);
MethodHandle bh = MethodHandles.insertArguments(h, 0, P);
assertEquals((long) bh.invokeExact(Q), R);
MethodHandle bbh = MethodHandles.insertArguments(bh, 0, Q);
assertEquals((long) bbh.invokeExact(), R);
MethodHandle b2h = MethodHandles.insertArguments(h, 1, Q);
assertEquals((long) b2h.invokeExact(P), R);
MethodHandle bb2h = MethodHandles.insertArguments(b2h, 0, P);
assertEquals((long) bb2h.invokeExact(), R);
}
}
@Test
public void testMaxInt() throws Throwable {
final Class<?> C = int.class;
final int P = 23;
final int Q = 42;
final int R = Math.max(P, Q);
for (int i = 0; i < ITERATION_COUNT; ++i) {
MethodHandle h = getMax(C);
assertEquals((int) h.invokeExact(P, Q), R);
MethodHandle bh = MethodHandles.insertArguments(h, 0, P);
assertEquals((int) bh.invokeExact(Q), R);
MethodHandle bbh = MethodHandles.insertArguments(bh, 0, Q);
assertEquals((int) bbh.invokeExact(), R);
MethodHandle b2h = MethodHandles.insertArguments(h, 1, Q);
assertEquals((int) b2h.invokeExact(P), R);
MethodHandle bb2h = MethodHandles.insertArguments(b2h, 0, P);
assertEquals((int) bb2h.invokeExact(), R);
}
}
@Test
public void testMaxFloat() throws Throwable {
final Class<?> C = float.class;
final float P = 23F;
final float Q = 42F;
final float R = Math.max(P, Q);
final float D = 0.1F;
for (int i = 0; i < ITERATION_COUNT; ++i) {
MethodHandle h = getMax(C);
assertEquals((float) h.invokeExact(P, Q), R, D);
MethodHandle bh = MethodHandles.insertArguments(h, 0, P);
assertEquals((float) bh.invokeExact(Q), R, D);
MethodHandle bbh = MethodHandles.insertArguments(bh, 0, Q);
assertEquals((float) bbh.invokeExact(), R, D);
MethodHandle b2h = MethodHandles.insertArguments(h, 1, Q);
assertEquals((float) b2h.invokeExact(P), R, D);
MethodHandle bb2h = MethodHandles.insertArguments(b2h, 0, P);
assertEquals((float) bb2h.invokeExact(), R, D);
}
}
@Test
public void testMaxDouble() throws Throwable {
final Class<?> C = double.class;
final double P = 23F;
final double Q = 42F;
final double R = Math.max(P, Q);
final double D = 0.1;
for (int i = 0; i < ITERATION_COUNT; ++i) {
MethodHandle h = getMax(C);
assertEquals((double) h.invokeExact(P, Q), R, D);
MethodHandle bh = MethodHandles.insertArguments(h, 0, P);
assertEquals((double) bh.invokeExact(Q), R, D);
MethodHandle bbh = MethodHandles.insertArguments(bh, 0, Q);
assertEquals((double) bbh.invokeExact(), R, D);
MethodHandle b2h = MethodHandles.insertArguments(h, 1, Q);
assertEquals((double) b2h.invokeExact(P), R, D);
MethodHandle bb2h = MethodHandles.insertArguments(b2h, 0, P);
assertEquals((double) bb2h.invokeExact(), R, D);
}
}
}
...@@ -1485,7 +1485,7 @@ public class MethodHandlesTest { ...@@ -1485,7 +1485,7 @@ public class MethodHandlesTest {
RuntimeException error = null; RuntimeException error = null;
try { try {
target = id.asType(newType); target = id.asType(newType);
} catch (RuntimeException ex) { } catch (WrongMethodTypeException ex) {
error = ex; error = ex;
} }
if (verbosity >= 3) if (verbosity >= 3)
...@@ -2381,47 +2381,100 @@ public class MethodHandlesTest { ...@@ -2381,47 +2381,100 @@ public class MethodHandlesTest {
assertSame(thrown, caught); assertSame(thrown, caught);
} }
//@Test @Test
public void testInterfaceCast() throws Throwable { public void testInterfaceCast() throws Throwable {
//if (CAN_SKIP_WORKING) return; //if (CAN_SKIP_WORKING) return;
startTest("interfaceCast"); startTest("interfaceCast");
for (Class<?> ctype : new Class<?>[]{ Object.class, String.class, CharSequence.class, Number.class, Iterable.class}) { assert( (((Object)"foo") instanceof CharSequence));
testInterfaceCast(ctype, false, false); assert(!(((Object)"foo") instanceof Iterable));
testInterfaceCast(ctype, true, false); for (MethodHandle mh : new MethodHandle[]{
testInterfaceCast(ctype, false, true); MethodHandles.identity(String.class),
testInterfaceCast(ctype, true, true); MethodHandles.identity(CharSequence.class),
MethodHandles.identity(Iterable.class)
}) {
if (verbosity > 0) System.out.println("-- mh = "+mh);
for (Class<?> ctype : new Class<?>[]{
Object.class, String.class, CharSequence.class,
Number.class, Iterable.class
}) {
if (verbosity > 0) System.out.println("---- ctype = "+ctype.getName());
// doret docast
testInterfaceCast(mh, ctype, false, false);
testInterfaceCast(mh, ctype, true, false);
testInterfaceCast(mh, ctype, false, true);
testInterfaceCast(mh, ctype, true, true);
}
} }
} }
public void testInterfaceCast(Class<?> ctype, boolean doret, boolean docast) throws Throwable { private static Class<?> i2o(Class<?> c) {
String str = "normal return value"; return (c.isInterface() ? Object.class : c);
MethodHandle mh = MethodHandles.identity(String.class); }
public void testInterfaceCast(MethodHandle mh, Class<?> ctype,
boolean doret, boolean docast) throws Throwable {
MethodHandle mh0 = mh;
if (verbosity > 1)
System.out.println("mh="+mh+", ctype="+ctype.getName()+", doret="+doret+", docast="+docast);
String normalRetVal = "normal return value";
MethodType mt = mh.type(); MethodType mt = mh.type();
MethodType mt0 = mt;
if (doret) mt = mt.changeReturnType(ctype); if (doret) mt = mt.changeReturnType(ctype);
else mt = mt.changeParameterType(0, ctype); else mt = mt.changeParameterType(0, ctype);
if (docast) mh = MethodHandles.explicitCastArguments(mh, mt); if (docast) mh = MethodHandles.explicitCastArguments(mh, mt);
else mh = mh.asType(mt); else mh = mh.asType(mt);
assertEquals(mt, mh.type());
MethodType mt1 = mt;
// this bit is needed to make the interface types disappear for invokeWithArguments: // this bit is needed to make the interface types disappear for invokeWithArguments:
mh = MethodHandles.explicitCastArguments(mh, mt.generic()); mh = MethodHandles.explicitCastArguments(mh, mt.generic());
boolean expectFail = !ctype.isInstance(str); Class<?>[] step = {
if (ctype.isInterface()) { mt1.parameterType(0), // param as passed to mh at first
// special rules: interfaces slide by more frequently mt0.parameterType(0), // param after incoming cast
if (docast || !doret) expectFail = false; mt0.returnType(), // return value before cast
mt1.returnType(), // return value after outgoing cast
};
// where might a checkCast occur?
boolean[] checkCast = new boolean[step.length];
// the string value must pass each step without causing an exception
if (!docast) {
if (!doret) {
if (step[0] != step[1])
checkCast[1] = true; // incoming value is cast
} else {
if (step[2] != step[3])
checkCast[3] = true; // outgoing value is cast
}
}
boolean expectFail = false;
for (int i = 0; i < step.length; i++) {
Class<?> c = step[i];
if (!checkCast[i]) c = i2o(c);
if (!c.isInstance(normalRetVal)) {
if (verbosity > 3)
System.out.println("expect failure at step "+i+" in "+Arrays.toString(step)+Arrays.toString(checkCast));
expectFail = true;
break;
}
} }
countTest(!expectFail);
if (verbosity > 2)
System.out.println("expectFail="+expectFail+", mt="+mt);
Object res; Object res;
try { try {
res = mh.invokeWithArguments(str); res = mh.invokeWithArguments(normalRetVal);
} catch (Exception ex) { } catch (Exception ex) {
res = ex; res = ex;
} }
boolean sawFail = !(res instanceof String); boolean sawFail = !(res instanceof String);
if (sawFail != expectFail) { if (sawFail != expectFail) {
System.out.println("*** testInterfaceCast: "+mh+" was "+mt+" => "+res+(docast ? " (explicitCastArguments)" : "")); System.out.println("*** testInterfaceCast: mh0 = "+mh0);
} System.out.println(" retype using "+(docast ? "explicitCastArguments" : "asType")+" to "+mt+" => "+mh);
if (!sawFail) { System.out.println(" call returned "+res);
assertFalse(res.toString(), expectFail); System.out.println(" expected "+(expectFail ? "an exception" : normalRetVal));
assertEquals(str, res); }
if (!expectFail) {
assertFalse(res.toString(), sawFail);
assertEquals(normalRetVal, res);
} else { } else {
assertTrue(res.toString(), expectFail); assertTrue(res.toString(), sawFail);
} }
} }
......
...@@ -191,7 +191,11 @@ public class PermuteArgsTest { ...@@ -191,7 +191,11 @@ public class PermuteArgsTest {
pt = mt1.parameterType(mt1.parameterCount() - posArgs); pt = mt1.parameterType(mt1.parameterCount() - posArgs);
mt1 = mt1.appendParameterTypes(pt); mt1 = mt1.appendParameterTypes(pt);
} }
return mh.asType(mt1); try {
return mh.asType(mt1);
} catch (WrongMethodTypeException | IllegalArgumentException ex) {
throw new IllegalArgumentException("cannot convert to type "+mt1+" from "+mh, ex);
}
} }
static MethodHandle findTestMH(String name, int[] perm) throws ReflectiveOperationException { static MethodHandle findTestMH(String name, int[] perm) throws ReflectiveOperationException {
int arity = perm.length; int arity = perm.length;
......
...@@ -256,7 +256,7 @@ public class RicochetTest { ...@@ -256,7 +256,7 @@ public class RicochetTest {
//System.out.println(" expect="+expect); //System.out.println(" expect="+expect);
// now use the combined MH, and test the output: // now use the combined MH, and test the output:
MethodHandle mh = collectArguments(lister, pos, INT_COLLECTORS[collects]); MethodHandle mh = collectArguments(lister, pos, int[].class, INT_COLLECTORS[collects]);
if (mh == null) continue; // no infix collection, yet if (mh == null) continue; // no infix collection, yet
assert(mh.type().parameterCount() == inputs); assert(mh.type().parameterCount() == inputs);
Object observe = mh.asSpreader(int[].class, args.length).invokeExact(args); Object observe = mh.asSpreader(int[].class, args.length).invokeExact(args);
...@@ -266,13 +266,53 @@ public class RicochetTest { ...@@ -266,13 +266,53 @@ public class RicochetTest {
} }
} }
private static MethodHandle collectArguments(MethodHandle lister, int pos, MethodHandle collector) { @Test
public void testByteCollects() throws Throwable {
if (!startTest("testByteCollects")) return;
for (MethodHandle lister : BYTE_LISTERS) {
int outputs = lister.type().parameterCount();
for (int collects = 0; collects <= Math.min(outputs, BYTE_COLLECTORS.length-1); collects++) {
int inputs = outputs - 1 + collects;
if (inputs < 0) continue;
for (int pos = 0; pos + collects <= inputs; pos++) {
MethodHandle collector = BYTE_COLLECTORS[collects];
byte[] args = new byte[inputs];
int ap = 0, arg = 31;
for (int i = 0; i < pos; i++)
args[ap++] = (byte)(arg++ + 0);
for (int i = 0; i < collects; i++)
args[ap++] = (byte)(arg++ + 10);
while (ap < args.length)
args[ap++] = (byte)(arg++ + 20);
// calculate piecemeal:
//System.out.println("testIntCollects "+Arrays.asList(lister, pos, collector)+" on "+Arrays.toString(args));
byte[] collargs = Arrays.copyOfRange(args, pos, pos+collects);
byte coll = (byte) collector.asSpreader(byte[].class, collargs.length).invokeExact(collargs);
byte[] listargs = Arrays.copyOfRange(args, 0, outputs);
System.arraycopy(args, pos+collects, listargs, pos+1, outputs - (pos+1));
listargs[pos] = coll;
//System.out.println(" coll="+coll+" listargs="+Arrays.toString(listargs));
Object expect = lister.asSpreader(byte[].class, listargs.length).invokeExact(listargs);
//System.out.println(" expect="+expect);
// now use the combined MH, and test the output:
MethodHandle mh = collectArguments(lister, pos, byte[].class, BYTE_COLLECTORS[collects]);
if (mh == null) continue; // no infix collection, yet
assert(mh.type().parameterCount() == inputs);
Object observe = mh.asSpreader(byte[].class, args.length).invokeExact(args);
assertEquals(expect, observe);
}
}
}
}
private static MethodHandle collectArguments(MethodHandle lister, int pos, Class<?> array, MethodHandle collector) {
int collects = collector.type().parameterCount(); int collects = collector.type().parameterCount();
int outputs = lister.type().parameterCount(); int outputs = lister.type().parameterCount();
if (pos == outputs - 1) if (pos == outputs - 1)
return MethodHandles.filterArguments(lister, pos, return MethodHandles.filterArguments(lister, pos,
collector.asSpreader(int[].class, collects)) collector.asSpreader(array, collects))
.asCollector(int[].class, collects); .asCollector(array, collects);
//return MethodHandles.collectArguments(lister, pos, collector); //no such animal //return MethodHandles.collectArguments(lister, pos, collector); //no such animal
return null; return null;
} }
...@@ -537,6 +577,9 @@ public class RicochetTest { ...@@ -537,6 +577,9 @@ public class RicochetTest {
private static final MethodHandle[] INT_COLLECTORS = { private static final MethodHandle[] INT_COLLECTORS = {
constant(int.class, 42), opI, opI2, opI3, opI4 constant(int.class, 42), opI, opI2, opI3, opI4
}; };
private static final MethodHandle[] BYTE_COLLECTORS = {
constant(byte.class, (byte)42), i2b(opI), i2b(opI2), i2b(opI3), i2b(opI4)
};
private static final MethodHandle[] LONG_COLLECTORS = { private static final MethodHandle[] LONG_COLLECTORS = {
constant(long.class, 42), opJ, opJ2, opJ3 constant(long.class, 42), opJ, opJ2, opJ3
}; };
...@@ -559,21 +602,36 @@ public class RicochetTest { ...@@ -559,21 +602,36 @@ public class RicochetTest {
Collections.nCopies(8, int.class)); Collections.nCopies(8, int.class));
private static final MethodHandle list8longs = findStatic("list8longs", Object.class, private static final MethodHandle list8longs = findStatic("list8longs", Object.class,
Collections.nCopies(8, long.class)); Collections.nCopies(8, long.class));
private static final MethodHandle[] INT_LISTERS, LONG_LISTERS; private static final MethodHandle[] INT_LISTERS, LONG_LISTERS, BYTE_LISTERS;
static { static {
int listerCount = list8ints.type().parameterCount() + 1; int listerCount = list8ints.type().parameterCount() + 1;
INT_LISTERS = new MethodHandle[listerCount]; INT_LISTERS = new MethodHandle[listerCount];
LONG_LISTERS = new MethodHandle[listerCount]; LONG_LISTERS = new MethodHandle[listerCount];
BYTE_LISTERS = new MethodHandle[listerCount];
MethodHandle lister = list8ints; MethodHandle lister = list8ints;
MethodHandle llister = list8longs; MethodHandle llister = list8longs;
for (int i = listerCount - 1; ; i--) { for (int i = listerCount - 1; ; i--) {
INT_LISTERS[i] = lister; INT_LISTERS[i] = lister;
LONG_LISTERS[i] = llister; LONG_LISTERS[i] = llister;
BYTE_LISTERS[i] = i2b(lister);
if (i == 0) break; if (i == 0) break;
lister = insertArguments(lister, i-1, 0); lister = insertArguments(lister, i-1, 0);
llister = insertArguments(llister, i-1, 0L); llister = insertArguments(llister, i-1, 0L);
} }
} }
private static MethodHandle i2b(MethodHandle mh) {
return MethodHandles.explicitCastArguments(mh, subst(mh.type(), int.class, byte.class));
}
private static MethodType subst(MethodType mt, Class<?> from, Class<?> to) {
for (int i = 0; i < mt.parameterCount(); i++) {
if (mt.parameterType(i) == from)
mt = mt.changeParameterType(i, to);
}
if (mt.returnType() == from)
mt = mt.changeReturnType(to);
return mt;
}
private static Object convI_L(int x) { stress(); return (Object) x; } private static Object convI_L(int x) { stress(); return (Object) x; }
private static int convL_I(Object x) { stress(); return (int) x; } private static int convL_I(Object x) { stress(); return (int) x; }
......
...@@ -159,14 +159,8 @@ public class ValueConversionsTest { ...@@ -159,14 +159,8 @@ public class ValueConversionsTest {
assertEquals(caster.type(), ValueConversions.identity().type()); assertEquals(caster.type(), ValueConversions.identity().type());
for (Object obj : objects) { for (Object obj : objects) {
Class<?> src = obj.getClass(); Class<?> src = obj.getClass();
boolean canCast; boolean canCast = dst.isAssignableFrom(src);
if (dst.isInterface()) { //System.out.println("obj="+obj+" <: dst="+dst+(canCast ? " (OK)" : " (will fail)"));
canCast = true;
} else {
canCast = dst.isAssignableFrom(src);
assertEquals(canCast, dst.isInstance(obj));
}
//System.out.println("obj="+obj+" <: dst="+dst);
try { try {
Object result = caster.invokeExact(obj); Object result = caster.invokeExact(obj);
if (canCast) if (canCast)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册