提交 3b5dc386 编写于 作者: J jcoomes

Merge

......@@ -142,6 +142,11 @@ import com.sun.xml.internal.ws.org.objectweb.asm.Type;
*/
protected abstract SpeciesData speciesData();
@Override
final Object internalProperties() {
return "/BMH="+internalValues();
}
@Override
final Object internalValues() {
Object[] boundValues = new Object[speciesData().fieldCount()];
......
......@@ -108,8 +108,8 @@ class DirectMethodHandle extends MethodHandle {
}
@Override
String debugString() {
return "DMH["+member.toString()+"]="+super.debugString();
String internalProperties() {
return "/DMH="+member.toString();
}
//// Implementation methods.
......
......@@ -185,12 +185,17 @@ class InvokerBytecodeGenerator {
}
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 @@ class InvokerBytecodeGenerator {
}
// 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 @@ class InvokerBytecodeGenerator {
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;
}
......
......@@ -74,8 +74,18 @@ class Invokers {
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 @@ class Invokers {
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 @@ class Invokers {
}
static MemberName invokeBasicMethod(MethodType type) {
type = type.basicType();
String name = "invokeBasic";
try {
//Lookup.findVirtual(MethodHandle.class, name, type);
......@@ -135,9 +157,31 @@ class Invokers {
/*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 @@ class Invokers {
.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 @@ class Invokers {
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 @@ class Invokers {
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 @@ class Invokers {
// 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 @@ class Invokers {
// 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 @@ class Invokers {
.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();
......
......@@ -596,14 +596,7 @@ class LambdaForm {
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 @@ class LambdaForm {
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 @@ class LambdaForm {
}
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];
......
......@@ -306,12 +306,6 @@ import java.util.Objects;
return this;
}
private void setFlags(int flags) {
this.flags = flags;
assert(testAnyFlags(ALL_KINDS));
assert(referenceKindIsConsistent());
}
private boolean testFlags(int mask, int value) {
return (flags & mask) == value;
}
......@@ -452,8 +446,10 @@ import java.util.Objects;
this.clazz = defClass;
this.name = name;
this.type = type;
setFlags(flags);
this.flags = flags;
assert(testAnyFlags(ALL_KINDS));
assert(this.resolution == null); // nobody should have touched this yet
//assert(referenceKindIsConsistent()); // do this after resolution
}
private void expandFromVM() {
......
......@@ -924,7 +924,7 @@ assertEquals("[123]", (String) longsToString.invokeExact((long)123));
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 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
return "MethodHandle"+type;
}
String debugString() {
return standardString()+"="+internalForm()+internalValues();
return standardString()+"/LF="+internalForm()+internalProperties();
}
//// Implementation methods.
......@@ -1269,6 +1269,12 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
/*non-public*/
Object internalValues() {
return null;
}
/*non-public*/
Object internalProperties() {
// Override to something like "/FOO=bar"
return "";
}
......
......@@ -59,7 +59,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
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 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
// 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);
// 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;
for (int i = 0, argIndex = 1, tmpIndex = lambdaType.parameterCount(); i < srcType.parameterCount(); i++, argIndex++) {
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 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
fn = ValueConversions.cast(dst);
}
}
names[tmpIndex] = new Name(fn, names[argIndex]);
indexes[i] = tmpIndex;
tmpIndex++;
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;
}
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) {
// synthesize a zero value for the given void
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 {
MethodHandle identity = MethodHandles.identity(needReturn);
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);
return new SimpleMethodHandle(srcType, form);
return SimpleMethodHandle.make(srcType, form);
}
static MethodHandle makeReferenceIdentity(Class<?> refType) {
......@@ -280,7 +296,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
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 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
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 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
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 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
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 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
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 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
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 {
......
......@@ -325,15 +325,28 @@ class MethodHandleNatives {
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 @@ class MethodHandleNatives {
}
}
/**
* 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.)
......
......@@ -1876,6 +1876,17 @@ assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY
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).
......
......@@ -111,6 +111,36 @@ class MethodType implements java.io.Serializable {
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 @@ class MethodType implements java.io.Serializable {
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) {
......
......@@ -35,10 +35,14 @@ import java.util.logging.Logger;
* @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);
......
......@@ -59,4 +59,27 @@ public class WrongMethodTypeException extends RuntimeException {
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);
}
}
此差异已折叠。
/*
* 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 {
RuntimeException error = null;
try {
target = id.asType(newType);
} catch (RuntimeException ex) {
} catch (WrongMethodTypeException ex) {
error = ex;
}
if (verbosity >= 3)
......@@ -2381,47 +2381,100 @@ public class MethodHandlesTest {
assertSame(thrown, caught);
}
//@Test
@Test
public void testInterfaceCast() throws Throwable {
//if (CAN_SKIP_WORKING) return;
startTest("interfaceCast");
for (Class<?> ctype : new Class<?>[]{ Object.class, String.class, CharSequence.class, Number.class, Iterable.class}) {
testInterfaceCast(ctype, false, false);
testInterfaceCast(ctype, true, false);
testInterfaceCast(ctype, false, true);
testInterfaceCast(ctype, true, true);
assert( (((Object)"foo") instanceof CharSequence));
assert(!(((Object)"foo") instanceof Iterable));
for (MethodHandle mh : new MethodHandle[]{
MethodHandles.identity(String.class),
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 {
String str = "normal return value";
MethodHandle mh = MethodHandles.identity(String.class);
private static Class<?> i2o(Class<?> c) {
return (c.isInterface() ? Object.class : c);
}
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 mt0 = mt;
if (doret) mt = mt.changeReturnType(ctype);
else mt = mt.changeParameterType(0, ctype);
if (docast) mh = MethodHandles.explicitCastArguments(mh, 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:
mh = MethodHandles.explicitCastArguments(mh, mt.generic());
boolean expectFail = !ctype.isInstance(str);
if (ctype.isInterface()) {
// special rules: interfaces slide by more frequently
if (docast || !doret) expectFail = false;
Class<?>[] step = {
mt1.parameterType(0), // param as passed to mh at first
mt0.parameterType(0), // param after incoming cast
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;
try {
res = mh.invokeWithArguments(str);
res = mh.invokeWithArguments(normalRetVal);
} catch (Exception ex) {
res = ex;
}
boolean sawFail = !(res instanceof String);
if (sawFail != expectFail) {
System.out.println("*** testInterfaceCast: "+mh+" was "+mt+" => "+res+(docast ? " (explicitCastArguments)" : ""));
}
if (!sawFail) {
assertFalse(res.toString(), expectFail);
assertEquals(str, res);
System.out.println("*** testInterfaceCast: mh0 = "+mh0);
System.out.println(" retype using "+(docast ? "explicitCastArguments" : "asType")+" to "+mt+" => "+mh);
System.out.println(" call returned "+res);
System.out.println(" expected "+(expectFail ? "an exception" : normalRetVal));
}
if (!expectFail) {
assertFalse(res.toString(), sawFail);
assertEquals(normalRetVal, res);
} else {
assertTrue(res.toString(), expectFail);
assertTrue(res.toString(), sawFail);
}
}
......
/*
* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 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
......@@ -25,7 +25,7 @@
/* @test
* @summary unit tests for method handles which permute their arguments
* @run junit/othervm -ea -esa -DPermuteArgsTest.MAX_ARITY=8 test.java.lang.invoke.PermuteArgsTest
* @run junit/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-VerifyDependencies -ea -esa -DPermuteArgsTest.MAX_ARITY=8 test.java.lang.invoke.PermuteArgsTest
*/
/* Examples of manual runs:
* java -DPermuteArgsTest.{DRY_RUN=true,MAX_ARITY=253} test.java.lang.invoke.PermuteArgsTest
......@@ -191,7 +191,11 @@ public class PermuteArgsTest {
pt = mt1.parameterType(mt1.parameterCount() - posArgs);
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 {
int arity = perm.length;
......
......@@ -256,7 +256,7 @@ public class RicochetTest {
//System.out.println(" expect="+expect);
// 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
assert(mh.type().parameterCount() == inputs);
Object observe = mh.asSpreader(int[].class, args.length).invokeExact(args);
......@@ -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 outputs = lister.type().parameterCount();
if (pos == outputs - 1)
return MethodHandles.filterArguments(lister, pos,
collector.asSpreader(int[].class, collects))
.asCollector(int[].class, collects);
collector.asSpreader(array, collects))
.asCollector(array, collects);
//return MethodHandles.collectArguments(lister, pos, collector); //no such animal
return null;
}
......@@ -537,6 +577,9 @@ public class RicochetTest {
private static final MethodHandle[] INT_COLLECTORS = {
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 = {
constant(long.class, 42), opJ, opJ2, opJ3
};
......@@ -559,21 +602,36 @@ public class RicochetTest {
Collections.nCopies(8, int.class));
private static final MethodHandle list8longs = findStatic("list8longs", Object.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 {
int listerCount = list8ints.type().parameterCount() + 1;
INT_LISTERS = new MethodHandle[listerCount];
LONG_LISTERS = new MethodHandle[listerCount];
BYTE_LISTERS = new MethodHandle[listerCount];
MethodHandle lister = list8ints;
MethodHandle llister = list8longs;
for (int i = listerCount - 1; ; i--) {
INT_LISTERS[i] = lister;
LONG_LISTERS[i] = llister;
BYTE_LISTERS[i] = i2b(lister);
if (i == 0) break;
lister = insertArguments(lister, i-1, 0);
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 int convL_I(Object x) { stress(); return (int) x; }
......
......@@ -159,14 +159,8 @@ public class ValueConversionsTest {
assertEquals(caster.type(), ValueConversions.identity().type());
for (Object obj : objects) {
Class<?> src = obj.getClass();
boolean canCast;
if (dst.isInterface()) {
canCast = true;
} else {
canCast = dst.isAssignableFrom(src);
assertEquals(canCast, dst.isInstance(obj));
}
//System.out.println("obj="+obj+" <: dst="+dst);
boolean canCast = dst.isAssignableFrom(src);
//System.out.println("obj="+obj+" <: dst="+dst+(canCast ? " (OK)" : " (will fail)"));
try {
Object result = caster.invokeExact(obj);
if (canCast)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册