提交 87496435 编写于 作者: J jrose

6939861: JVM should handle more conversion operations

Summary: Integrate JDK code with JVM-supplied ricochet frames.
Reviewed-by: never, twisti
上级 15350370
...@@ -61,6 +61,10 @@ class FilterGeneric { ...@@ -61,6 +61,10 @@ class FilterGeneric {
return ad; return ad;
} }
static {
assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this class is deprecated
}
Adapter makeInstance(Kind kind, int pos, MethodHandle filter, MethodHandle target) { Adapter makeInstance(Kind kind, int pos, MethodHandle filter, MethodHandle target) {
Adapter ad = getAdapter(kind, pos); Adapter ad = getAdapter(kind, pos);
return ad.makeInstance(ad.prototypeEntryPoint(), filter, target); return ad.makeInstance(ad.prototypeEntryPoint(), filter, target);
......
...@@ -67,6 +67,10 @@ class FilterOneArgument extends BoundMethodHandle { ...@@ -67,6 +67,10 @@ class FilterOneArgument extends BoundMethodHandle {
this.target = target; this.target = target;
} }
static {
assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this class is deprecated
}
public static MethodHandle make(MethodHandle filter, MethodHandle target) { public static MethodHandle make(MethodHandle filter, MethodHandle target) {
if (filter == null) return target; if (filter == null) return target;
if (target == null) return filter; if (target == null) return filter;
......
...@@ -98,6 +98,10 @@ class FromGeneric { ...@@ -98,6 +98,10 @@ class FromGeneric {
this.unboxingInvoker = computeUnboxingInvoker(targetType, internalType0); this.unboxingInvoker = computeUnboxingInvoker(targetType, internalType0);
} }
static {
assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this class is deprecated
}
/** /**
* The typed target will be called according to targetType. * The typed target will be called according to targetType.
* The adapter code will in fact see the raw result from internalType, * The adapter code will in fact see the raw result from internalType,
...@@ -112,10 +116,10 @@ class FromGeneric { ...@@ -112,10 +116,10 @@ class FromGeneric {
assert(iret == Object.class); assert(iret == Object.class);
return ValueConversions.identity(); return ValueConversions.identity();
} else if (wrap.primitiveType() == iret) { } else if (wrap.primitiveType() == iret) {
return ValueConversions.box(wrap, false); return ValueConversions.box(wrap);
} else { } else {
assert(tret == double.class ? iret == long.class : iret == int.class); assert(tret == double.class ? iret == long.class : iret == int.class);
return ValueConversions.boxRaw(wrap, false); return ValueConversions.boxRaw(wrap);
} }
} }
...@@ -135,7 +139,7 @@ class FromGeneric { ...@@ -135,7 +139,7 @@ class FromGeneric {
MethodType fixArgsType = internalType.changeReturnType(targetType.returnType()); MethodType fixArgsType = internalType.changeReturnType(targetType.returnType());
MethodHandle fixArgs = MethodHandleImpl.convertArguments( MethodHandle fixArgs = MethodHandleImpl.convertArguments(
invoker, Invokers.invokerType(fixArgsType), invoker, Invokers.invokerType(fixArgsType),
invoker.type(), null); invoker.type(), 0);
if (fixArgs == null) if (fixArgs == null)
throw new InternalError("bad fixArgs"); throw new InternalError("bad fixArgs");
// reinterpret the calling sequence as raw: // reinterpret the calling sequence as raw:
...@@ -230,7 +234,7 @@ class FromGeneric { ...@@ -230,7 +234,7 @@ class FromGeneric {
} }
static Adapter buildAdapterFromBytecodes(MethodType internalType) { static Adapter buildAdapterFromBytecodes(MethodType internalType) {
throw new UnsupportedOperationException("NYI"); throw new UnsupportedOperationException("NYI "+internalType);
} }
/** /**
......
...@@ -82,7 +82,7 @@ class Invokers { ...@@ -82,7 +82,7 @@ class Invokers {
MethodHandle invoker = generalInvoker; MethodHandle invoker = generalInvoker;
if (invoker != null) return invoker; if (invoker != null) return invoker;
MethodType generalType = targetType.generic(); MethodType generalType = targetType.generic();
invoker = MethodHandles.convertArguments(invoker1, invokerType(generalType)); invoker = invoker1.asType(invokerType(generalType));
generalInvoker = invoker; generalInvoker = invoker;
return invoker; return invoker;
} }
...@@ -95,7 +95,7 @@ class Invokers { ...@@ -95,7 +95,7 @@ class Invokers {
if (erasedType == targetType.generic()) if (erasedType == targetType.generic())
invoker = generalInvoker(); invoker = generalInvoker();
else else
invoker = MethodHandles.convertArguments(invoker1, invokerType(erasedType)); invoker = invoker1.asType(invokerType(erasedType));
erasedInvoker = invoker; erasedInvoker = invoker;
return invoker; return invoker;
} }
......
...@@ -525,7 +525,7 @@ import static java.lang.invoke.MethodHandleStatics.*; ...@@ -525,7 +525,7 @@ import static java.lang.invoke.MethodHandleStatics.*;
/** A factory type for resolving member names with the help of the VM. /** A factory type for resolving member names with the help of the VM.
* TBD: Define access-safe public constructors for this factory. * TBD: Define access-safe public constructors for this factory.
*/ */
public static class Factory { /*non-public*/ static class Factory {
private Factory() { } // singleton pattern private Factory() { } // singleton pattern
static Factory INSTANCE = new Factory(); static Factory INSTANCE = new Factory();
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
package java.lang.invoke; package java.lang.invoke;
import sun.invoke.util.ValueConversions;
import static java.lang.invoke.MethodHandleStatics.*; import static java.lang.invoke.MethodHandleStatics.*;
/** /**
...@@ -706,6 +707,9 @@ public abstract class MethodHandle { ...@@ -706,6 +707,9 @@ public abstract class MethodHandle {
* @see MethodHandles#convertArguments * @see MethodHandles#convertArguments
*/ */
public MethodHandle asType(MethodType newType) { public MethodHandle asType(MethodType newType) {
if (!type.isConvertibleTo(newType)) {
throw new WrongMethodTypeException("cannot convert "+type+" to "+newType);
}
return MethodHandles.convertArguments(this, newType); return MethodHandles.convertArguments(this, newType);
} }
...@@ -748,13 +752,9 @@ public abstract class MethodHandle { ...@@ -748,13 +752,9 @@ public abstract class MethodHandle {
public MethodHandle asSpreader(Class<?> arrayType, int arrayLength) { public MethodHandle asSpreader(Class<?> arrayType, int arrayLength) {
Class<?> arrayElement = arrayType.getComponentType(); Class<?> arrayElement = arrayType.getComponentType();
if (arrayElement == null) throw newIllegalArgumentException("not an array type"); if (arrayElement == null) throw newIllegalArgumentException("not an array type");
MethodType oldType = type(); int nargs = type().parameterCount();
int nargs = oldType.parameterCount();
if (nargs < arrayLength) throw newIllegalArgumentException("bad spread array length"); if (nargs < arrayLength) throw newIllegalArgumentException("bad spread array length");
int keepPosArgs = nargs - arrayLength; return MethodHandleImpl.spreadArguments(this, arrayType, arrayLength);
MethodType newType = oldType.dropParameterTypes(keepPosArgs, nargs);
newType = newType.insertParameterTypes(keepPosArgs, arrayType);
return MethodHandles.spreadArguments(this, newType);
} }
/** /**
...@@ -797,15 +797,18 @@ public abstract class MethodHandle { ...@@ -797,15 +797,18 @@ public abstract class MethodHandle {
* @see #asVarargsCollector * @see #asVarargsCollector
*/ */
public MethodHandle asCollector(Class<?> arrayType, int arrayLength) { public MethodHandle asCollector(Class<?> arrayType, int arrayLength) {
asCollectorChecks(arrayType, arrayLength);
MethodHandle collector = ValueConversions.varargsArray(arrayType, arrayLength);
return MethodHandleImpl.collectArguments(this, type.parameterCount()-1, collector);
}
private void asCollectorChecks(Class<?> arrayType, int arrayLength) {
Class<?> arrayElement = arrayType.getComponentType(); Class<?> arrayElement = arrayType.getComponentType();
if (arrayElement == null) throw newIllegalArgumentException("not an array type"); if (arrayElement == null)
MethodType oldType = type(); throw newIllegalArgumentException("not an array type", arrayType);
int nargs = oldType.parameterCount(); int nargs = type().parameterCount();
if (nargs == 0) throw newIllegalArgumentException("no trailing argument"); if (nargs == 0 || !type().parameterType(nargs-1).isAssignableFrom(arrayType))
MethodType newType = oldType.dropParameterTypes(nargs-1, nargs); throw newIllegalArgumentException("array type not assignable to trailing argument", this, arrayType);
newType = newType.insertParameterTypes(nargs-1,
java.util.Collections.<Class<?>>nCopies(arrayLength, arrayElement));
return MethodHandles.collectArguments(this, newType);
} }
/** /**
......
...@@ -115,6 +115,8 @@ class MethodHandleNatives { ...@@ -115,6 +115,8 @@ class MethodHandleNatives {
/** Which conv-ops are implemented by the JVM? */ /** Which conv-ops are implemented by the JVM? */
static final int CONV_OP_IMPLEMENTED_MASK; static final int CONV_OP_IMPLEMENTED_MASK;
/** Derived mode flag. Only false on some old JVM implementations. */
static final boolean HAVE_RICOCHET_FRAMES;
private static native void registerNatives(); private static native void registerNatives();
static { static {
...@@ -141,6 +143,7 @@ class MethodHandleNatives { ...@@ -141,6 +143,7 @@ class MethodHandleNatives {
if (CONV_OP_IMPLEMENTED_MASK_ == 0) if (CONV_OP_IMPLEMENTED_MASK_ == 0)
CONV_OP_IMPLEMENTED_MASK_ = DEFAULT_CONV_OP_IMPLEMENTED_MASK; CONV_OP_IMPLEMENTED_MASK_ = DEFAULT_CONV_OP_IMPLEMENTED_MASK;
CONV_OP_IMPLEMENTED_MASK = CONV_OP_IMPLEMENTED_MASK_; CONV_OP_IMPLEMENTED_MASK = CONV_OP_IMPLEMENTED_MASK_;
HAVE_RICOCHET_FRAMES = (CONV_OP_IMPLEMENTED_MASK & (1<<OP_COLLECT_ARGS)) != 0;
} }
// All compile-time constants go here. // All compile-time constants go here.
...@@ -186,25 +189,26 @@ class MethodHandleNatives { ...@@ -186,25 +189,26 @@ class MethodHandleNatives {
*/ */
static final int static final int
OP_RETYPE_ONLY = 0x0, // no argument changes; straight retype OP_RETYPE_ONLY = 0x0, // no argument changes; straight retype
OP_RETYPE_RAW = 0x1, // no argument changes; straight retype OP_RETYPE_RAW = 0x1, // straight retype, trusted (void->int, Object->T)
OP_CHECK_CAST = 0x2, // ref-to-ref conversion; requires a Class argument OP_CHECK_CAST = 0x2, // ref-to-ref conversion; requires a Class argument
OP_PRIM_TO_PRIM = 0x3, // converts from one primitive to another OP_PRIM_TO_PRIM = 0x3, // converts from one primitive to another
OP_REF_TO_PRIM = 0x4, // unboxes a wrapper to produce a primitive OP_REF_TO_PRIM = 0x4, // unboxes a wrapper to produce a primitive
OP_PRIM_TO_REF = 0x5, // boxes a primitive into a wrapper (NYI) OP_PRIM_TO_REF = 0x5, // boxes a primitive into a wrapper
OP_SWAP_ARGS = 0x6, // swap arguments (vminfo is 2nd arg) OP_SWAP_ARGS = 0x6, // swap arguments (vminfo is 2nd arg)
OP_ROT_ARGS = 0x7, // rotate arguments (vminfo is displaced arg) OP_ROT_ARGS = 0x7, // rotate arguments (vminfo is displaced arg)
OP_DUP_ARGS = 0x8, // duplicates one or more arguments (at TOS) OP_DUP_ARGS = 0x8, // duplicates one or more arguments (at TOS)
OP_DROP_ARGS = 0x9, // remove one or more argument slots OP_DROP_ARGS = 0x9, // remove one or more argument slots
OP_COLLECT_ARGS = 0xA, // combine one or more arguments into a varargs (NYI) OP_COLLECT_ARGS = 0xA, // combine arguments using an auxiliary function
OP_SPREAD_ARGS = 0xB, // expand in place a varargs array (of known size) OP_SPREAD_ARGS = 0xB, // expand in place a varargs array (of known size)
OP_FLYBY = 0xC, // operate first on reified argument list (NYI) OP_FOLD_ARGS = 0xC, // combine but do not remove arguments; prepend result
OP_RICOCHET = 0xD, // run an adapter chain on the return value (NYI) //OP_UNUSED_13 = 0xD, // unused code, perhaps for reified argument lists
CONV_OP_LIMIT = 0xE; // limit of CONV_OP enumeration CONV_OP_LIMIT = 0xE; // limit of CONV_OP enumeration
/** Shift and mask values for decoding the AMH.conversion field. /** Shift and mask values for decoding the AMH.conversion field.
* These numbers are shared with the JVM for creating AMHs. * These numbers are shared with the JVM for creating AMHs.
*/ */
static final int static final int
CONV_OP_MASK = 0xF00, // this nybble contains the conversion op field CONV_OP_MASK = 0xF00, // this nybble contains the conversion op field
CONV_TYPE_MASK = 0x0F, // fits T_ADDRESS and below
CONV_VMINFO_MASK = 0x0FF, // LSB is reserved for JVM use CONV_VMINFO_MASK = 0x0FF, // LSB is reserved for JVM use
CONV_VMINFO_SHIFT = 0, // position of bits in CONV_VMINFO_MASK CONV_VMINFO_SHIFT = 0, // position of bits in CONV_VMINFO_MASK
CONV_OP_SHIFT = 8, // position of bits in CONV_OP_MASK CONV_OP_SHIFT = 8, // position of bits in CONV_OP_MASK
...@@ -244,8 +248,9 @@ class MethodHandleNatives { ...@@ -244,8 +248,9 @@ class MethodHandleNatives {
T_LONG = 11, T_LONG = 11,
T_OBJECT = 12, T_OBJECT = 12,
//T_ARRAY = 13 //T_ARRAY = 13
T_VOID = 14; T_VOID = 14,
//T_ADDRESS = 15 //T_ADDRESS = 15
T_ILLEGAL = 99;
/** /**
* Constant pool reference-kind codes, as used by CONSTANT_MethodHandle CP entries. * Constant pool reference-kind codes, as used by CONSTANT_MethodHandle CP entries.
...@@ -273,16 +278,29 @@ class MethodHandleNatives { ...@@ -273,16 +278,29 @@ class MethodHandleNatives {
try { try {
Field con = Constants.class.getDeclaredField(name); Field con = Constants.class.getDeclaredField(name);
int jval = con.getInt(null); int jval = con.getInt(null);
if (jval != vmval) if (jval == vmval) continue;
throw new InternalError(name+": JVM has "+vmval+" while Java has "+jval); String err = (name+": JVM has "+vmval+" while Java has "+jval);
if (name.equals("CONV_OP_LIMIT")) {
System.err.println("warning: "+err);
continue;
}
throw new InternalError(err);
} catch (Exception ex) { } catch (Exception ex) {
if (ex instanceof NoSuchFieldException) {
String err = (name+": JVM has "+vmval+" which Java does not define");
// ignore exotic ops the JVM cares about; we just wont issue them
if (name.startsWith("OP_") || name.startsWith("GC_")) {
System.err.println("warning: "+err);
continue;
}
}
throw new InternalError(name+": access failed, got "+ex); throw new InternalError(name+": access failed, got "+ex);
} }
} }
return true; return true;
} }
static { static {
verifyConstants(); assert(verifyConstants());
} }
// Up-calls from the JVM. // Up-calls from the JVM.
...@@ -323,15 +341,39 @@ class MethodHandleNatives { ...@@ -323,15 +341,39 @@ class MethodHandleNatives {
* The JVM wants to raise an exception. Here's the path. * The JVM wants to raise an exception. Here's the path.
*/ */
static void raiseException(int code, Object actual, Object required) { static void raiseException(int code, Object actual, Object required) {
String message; String message = null;
switch (code) {
case 190: // arraylength
try {
String reqLength = "";
if (required instanceof AdapterMethodHandle) {
int conv = ((AdapterMethodHandle)required).getConversion();
int spChange = AdapterMethodHandle.extractStackMove(conv);
reqLength = " of length "+(spChange+1);
}
int actualLength = actual == null ? 0 : java.lang.reflect.Array.getLength(actual);
message = "required array"+reqLength+", but encountered wrong length "+actualLength;
break;
} catch (IllegalArgumentException ex) {
}
required = Object[].class; // should have been an array
code = 192; // checkcast
break;
}
// disregard the identity of the actual object, if it is not a class: // disregard the identity of the actual object, if it is not a class:
if (!(actual instanceof Class) && !(actual instanceof MethodType)) if (message == null) {
actual = actual.getClass(); if (!(actual instanceof Class) && !(actual instanceof MethodType))
if (actual != null) actual = actual.getClass();
message = "required "+required+" but encountered "+actual; if (actual != null)
else message = "required "+required+" but encountered "+actual;
message = "required "+required; else
message = "required "+required;
}
switch (code) { switch (code) {
case 190: // arraylength
throw new ArrayIndexOutOfBoundsException(message);
case 50: //_aaload
throw new ClassCastException(message);
case 192: // checkcast case 192: // checkcast
throw new ClassCastException(message); throw new ClassCastException(message);
default: default:
...@@ -365,4 +407,13 @@ class MethodHandleNatives { ...@@ -365,4 +407,13 @@ class MethodHandleNatives {
throw err; throw err;
} }
} }
/**
* This assertion marks code which was written before ricochet frames were implemented.
* Such code will go away when the ports catch up.
*/
static boolean workaroundWithoutRicochetFrames() {
assert(!HAVE_RICOCHET_FRAMES) : "this code should not be executed if `-XX:+UseRicochetFrames is enabled";
return true;
}
} }
...@@ -63,8 +63,17 @@ package java.lang.invoke; ...@@ -63,8 +63,17 @@ package java.lang.invoke;
} }
static void checkSpreadArgument(Object av, int n) { static void checkSpreadArgument(Object av, int n) {
if (av == null ? n != 0 : ((Object[])av).length != n) if (av == null) {
throw newIllegalArgumentException("Array is not of length "+n); if (n == 0) return;
} else if (av instanceof Object[]) {
int len = ((Object[])av).length;
if (len == n) return;
} else {
int len = java.lang.reflect.Array.getLength(av);
if (len == n) return;
}
// fall through to error:
throw newIllegalArgumentException("Array is not of length "+n);
} }
// handy shared exception makers (they simplify the common case code) // handy shared exception makers (they simplify the common case code)
...@@ -80,6 +89,9 @@ package java.lang.invoke; ...@@ -80,6 +89,9 @@ package java.lang.invoke;
/*non-public*/ static RuntimeException newIllegalArgumentException(String message, Object obj) { /*non-public*/ static RuntimeException newIllegalArgumentException(String message, Object obj) {
return new IllegalArgumentException(message(message, obj)); return new IllegalArgumentException(message(message, obj));
} }
/*non-public*/ static RuntimeException newIllegalArgumentException(String message, Object obj, Object obj2) {
return new IllegalArgumentException(message(message, obj, obj2));
}
/*non-public*/ static Error uncaughtException(Exception ex) { /*non-public*/ static Error uncaughtException(Exception ex) {
Error err = new InternalError("uncaught exception"); Error err = new InternalError("uncaught exception");
err.initCause(ex); err.initCause(ex);
...@@ -89,4 +101,8 @@ package java.lang.invoke; ...@@ -89,4 +101,8 @@ package java.lang.invoke;
if (obj != null) message = message + ": " + obj; if (obj != null) message = message + ": " + obj;
return message; return message;
} }
private static String message(String message, Object obj, Object obj2) {
if (obj != null || obj2 != null) message = message + ": " + obj + ", " + obj2;
return message;
}
} }
...@@ -1080,7 +1080,7 @@ return mh1; ...@@ -1080,7 +1080,7 @@ return mh1;
MethodType rawType = mh.type(); MethodType rawType = mh.type();
if (rawType.parameterType(0) == caller) return mh; if (rawType.parameterType(0) == caller) return mh;
MethodType narrowType = rawType.changeParameterType(0, caller); MethodType narrowType = rawType.changeParameterType(0, caller);
MethodHandle narrowMH = MethodHandleImpl.convertArguments(mh, narrowType, rawType, null); MethodHandle narrowMH = MethodHandleImpl.convertArguments(mh, narrowType, rawType, 0);
return fixVarargs(narrowMH, mh); return fixVarargs(narrowMH, mh);
} }
...@@ -1377,18 +1377,7 @@ publicLookup().findVirtual(MethodHandle.class, "invoke", type) ...@@ -1377,18 +1377,7 @@ publicLookup().findVirtual(MethodHandle.class, "invoke", type)
*/ */
public static public static
MethodHandle convertArguments(MethodHandle target, MethodType newType) { MethodHandle convertArguments(MethodHandle target, MethodType newType) {
MethodType oldType = target.type(); return MethodHandleImpl.convertArguments(target, newType, 1);
if (oldType.equals(newType))
return target;
MethodHandle res = null;
try {
res = MethodHandleImpl.convertArguments(target,
newType, oldType, null);
} catch (IllegalArgumentException ex) {
}
if (res == null)
throw new WrongMethodTypeException("cannot convert to "+newType+": "+target);
return res;
} }
/** /**
...@@ -1431,7 +1420,7 @@ publicLookup().findVirtual(MethodHandle.class, "invoke", type) ...@@ -1431,7 +1420,7 @@ publicLookup().findVirtual(MethodHandle.class, "invoke", type)
*/ */
public static public static
MethodHandle explicitCastArguments(MethodHandle target, MethodType newType) { MethodHandle explicitCastArguments(MethodHandle target, MethodType newType) {
return convertArguments(target, newType); // FIXME! return MethodHandleImpl.convertArguments(target, newType, 2);
} }
/* /*
...@@ -1526,23 +1515,32 @@ assert((int)twice.invokeExact(21) == 42); ...@@ -1526,23 +1515,32 @@ assert((int)twice.invokeExact(21) == 42);
MethodHandle permuteArguments(MethodHandle target, MethodType newType, int... reorder) { MethodHandle permuteArguments(MethodHandle target, MethodType newType, int... reorder) {
MethodType oldType = target.type(); MethodType oldType = target.type();
checkReorder(reorder, newType, oldType); checkReorder(reorder, newType, oldType);
return MethodHandleImpl.convertArguments(target, return MethodHandleImpl.permuteArguments(target,
newType, oldType, newType, oldType,
reorder); reorder);
} }
private static void checkReorder(int[] reorder, MethodType newType, MethodType oldType) { private static void checkReorder(int[] reorder, MethodType newType, MethodType oldType) {
if (newType.returnType() != oldType.returnType())
throw newIllegalArgumentException("return types do not match",
oldType, newType);
if (reorder.length == oldType.parameterCount()) { if (reorder.length == oldType.parameterCount()) {
int limit = newType.parameterCount(); int limit = newType.parameterCount();
boolean bad = false; boolean bad = false;
for (int i : reorder) { for (int j = 0; j < reorder.length; j++) {
int i = reorder[j];
if (i < 0 || i >= limit) { if (i < 0 || i >= limit) {
bad = true; break; bad = true; break;
} }
Class<?> src = newType.parameterType(i);
Class<?> dst = oldType.parameterType(j);
if (src != dst)
throw newIllegalArgumentException("parameter types do not match after reorder",
oldType, newType);
} }
if (!bad) return; if (!bad) return;
} }
throw newIllegalArgumentException("bad reorder array"); throw newIllegalArgumentException("bad reorder array: "+Arrays.toString(reorder));
} }
/** /**
...@@ -1631,7 +1629,7 @@ assert((int)twice.invokeExact(21) == 42); ...@@ -1631,7 +1629,7 @@ assert((int)twice.invokeExact(21) == 42);
if (type == void.class) if (type == void.class)
throw newIllegalArgumentException("void type"); throw newIllegalArgumentException("void type");
Wrapper w = Wrapper.forPrimitiveType(type); Wrapper w = Wrapper.forPrimitiveType(type);
return identity(type).bindTo(w.convert(value, type)); return insertArguments(identity(type), 0, w.convert(value, type));
} else { } else {
return identity(type).bindTo(type.cast(value)); return identity(type).bindTo(type.cast(value));
} }
...@@ -1866,7 +1864,8 @@ assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY ...@@ -1866,7 +1864,8 @@ assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY
MethodHandle filterArguments(MethodHandle target, int pos, MethodHandle... filters) { MethodHandle filterArguments(MethodHandle target, int pos, MethodHandle... filters) {
MethodType targetType = target.type(); MethodType targetType = target.type();
MethodHandle adapter = target; MethodHandle adapter = target;
MethodType adapterType = targetType; MethodType adapterType = null;
assert((adapterType = targetType) != null);
int maxPos = targetType.parameterCount(); int maxPos = targetType.parameterCount();
if (pos + filters.length > maxPos) if (pos + filters.length > maxPos)
throw newIllegalArgumentException("too many filters"); throw newIllegalArgumentException("too many filters");
...@@ -1874,19 +1873,23 @@ assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY ...@@ -1874,19 +1873,23 @@ assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY
for (MethodHandle filter : filters) { for (MethodHandle filter : filters) {
curPos += 1; curPos += 1;
if (filter == null) continue; // ignore null elements of filters if (filter == null) continue; // ignore null elements of filters
MethodType filterType = filter.type(); adapter = filterArgument(adapter, curPos, filter);
if (filterType.parameterCount() != 1 assert((adapterType = adapterType.changeParameterType(curPos, filter.type().parameterType(0))) != null);
|| filterType.returnType() != targetType.parameterType(curPos))
throw newIllegalArgumentException("target and filter types do not match");
adapterType = adapterType.changeParameterType(curPos, filterType.parameterType(0));
adapter = MethodHandleImpl.filterArgument(adapter, curPos, filter);
} }
MethodType midType = adapter.type(); assert(adapterType.equals(adapter.type()));
if (midType != adapterType)
adapter = MethodHandleImpl.convertArguments(adapter, adapterType, midType, null);
return adapter; return adapter;
} }
/*non-public*/ static
MethodHandle filterArgument(MethodHandle target, int pos, MethodHandle filter) {
MethodType targetType = target.type();
MethodType filterType = filter.type();
if (filterType.parameterCount() != 1
|| filterType.returnType() != targetType.parameterType(pos))
throw newIllegalArgumentException("target and filter types do not match", targetType, filterType);
return MethodHandleImpl.filterArgument(target, pos, filter);
}
/** /**
* Adapts a target method handle {@code target} by post-processing * Adapts a target method handle {@code target} by post-processing
* its return value with a unary filter function. * its return value with a unary filter function.
...@@ -1922,14 +1925,26 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2 ...@@ -1922,14 +1925,26 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2
MethodHandle filterReturnValue(MethodHandle target, MethodHandle filter) { MethodHandle filterReturnValue(MethodHandle target, MethodHandle filter) {
MethodType targetType = target.type(); MethodType targetType = target.type();
MethodType filterType = filter.type(); MethodType filterType = filter.type();
if (filterType.parameterCount() != 1 Class<?> rtype = targetType.returnType();
|| filterType.parameterType(0) != targetType.returnType()) int filterValues = filterType.parameterCount();
throw newIllegalArgumentException("target and filter types do not match"); if (filterValues == 0
? (rtype != void.class)
: (rtype != filterType.parameterType(0)))
throw newIllegalArgumentException("target and filter types do not match", target, filter);
// result = fold( lambda(retval, arg...) { filter(retval) }, // result = fold( lambda(retval, arg...) { filter(retval) },
// lambda( arg...) { target(arg...) } ) // lambda( arg...) { target(arg...) } )
MethodType newType = targetType.changeReturnType(filterType.returnType());
MethodHandle result = null;
if (AdapterMethodHandle.canCollectArguments(filterType, targetType, 0, false)) {
result = AdapterMethodHandle.makeCollectArguments(filter, target, 0, false);
if (result != null) return result;
}
// FIXME: Too many nodes here. // FIXME: Too many nodes here.
MethodHandle returner = dropArguments(filter, 1, targetType.parameterList()); assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this class is deprecated
return foldArguments(returner, target); MethodHandle returner = dropArguments(filter, filterValues, targetType.parameterList());
result = foldArguments(returner, target);
assert(result.type().equals(newType));
return result;
} }
/** /**
...@@ -1981,16 +1996,23 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2 ...@@ -1981,16 +1996,23 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2
MethodHandle foldArguments(MethodHandle target, MethodHandle combiner) { MethodHandle foldArguments(MethodHandle target, MethodHandle combiner) {
MethodType targetType = target.type(); MethodType targetType = target.type();
MethodType combinerType = combiner.type(); MethodType combinerType = combiner.type();
int foldPos = 0; // always at the head, at present
int foldArgs = combinerType.parameterCount(); int foldArgs = combinerType.parameterCount();
boolean ok = (targetType.parameterCount() >= 1 + foldArgs); int foldVals = combinerType.returnType() == void.class ? 0 : 1;
if (ok && !combinerType.parameterList().equals(targetType.parameterList().subList(1, foldArgs+1))) int afterInsertPos = foldPos + foldVals;
boolean ok = (targetType.parameterCount() >= afterInsertPos + foldArgs);
if (ok && !(combinerType.parameterList()
.equals(targetType.parameterList().subList(afterInsertPos,
afterInsertPos + foldArgs))))
ok = false; ok = false;
if (ok && !combinerType.returnType().equals(targetType.parameterType(0))) if (ok && foldVals != 0 && !combinerType.returnType().equals(targetType.parameterType(0)))
ok = false; ok = false;
if (!ok) if (!ok)
throw misMatchedTypes("target and combiner types", targetType, combinerType); throw misMatchedTypes("target and combiner types", targetType, combinerType);
MethodType newType = targetType.dropParameterTypes(0, 1); MethodType newType = targetType.dropParameterTypes(foldPos, afterInsertPos);
return MethodHandleImpl.foldArguments(target, newType, combiner); MethodHandle res = MethodHandleImpl.foldArguments(target, newType, foldPos, combiner);
if (res == null) throw newIllegalArgumentException("cannot fold from "+newType+" to " +targetType);
return res;
} }
/** /**
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
package java.lang.invoke; package java.lang.invoke;
import sun.invoke.util.Wrapper;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
...@@ -262,18 +263,18 @@ class MethodType implements java.io.Serializable { ...@@ -262,18 +263,18 @@ class MethodType implements java.io.Serializable {
* Finds or creates a method type whose components are {@code Object} with an optional trailing {@code Object[]} array. * Finds or creates a method type whose components are {@code Object} with an optional trailing {@code Object[]} array.
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}. * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
* All parameters and the return type will be {@code Object}, * All parameters and the return type will be {@code Object},
* except the final varargs parameter if any, which will be {@code Object[]}. * except the final array parameter if any, which will be {@code Object[]}.
* @param objectArgCount number of parameters (excluding the varargs parameter if any) * @param objectArgCount number of parameters (excluding the final array parameter if any)
* @param varargs whether there will be a varargs parameter, of type {@code Object[]} * @param finalArray whether there will be a trailing array parameter, of type {@code Object[]}
* @return a totally generic method type, given only its count of parameters and varargs * @return a generally applicable method type, for all calls of the given fixed argument count and a collected array of further arguments
* @throws IllegalArgumentException if {@code objectArgCount} is negative or greater than 255 * @throws IllegalArgumentException if {@code objectArgCount} is negative or greater than 255 (or 254, if {@code finalArray})
* @see #genericMethodType(int) * @see #genericMethodType(int)
*/ */
public static public static
MethodType genericMethodType(int objectArgCount, boolean varargs) { MethodType genericMethodType(int objectArgCount, boolean finalArray) {
MethodType mt; MethodType mt;
checkSlotCount(objectArgCount); checkSlotCount(objectArgCount);
int ivarargs = (!varargs ? 0 : 1); int ivarargs = (!finalArray ? 0 : 1);
int ootIndex = objectArgCount*2 + ivarargs; int ootIndex = objectArgCount*2 + ivarargs;
if (ootIndex < objectOnlyTypes.length) { if (ootIndex < objectOnlyTypes.length) {
mt = objectOnlyTypes[ootIndex]; mt = objectOnlyTypes[ootIndex];
...@@ -626,6 +627,30 @@ class MethodType implements java.io.Serializable { ...@@ -626,6 +627,30 @@ class MethodType implements java.io.Serializable {
return sb.toString(); return sb.toString();
} }
/*non-public*/
boolean isConvertibleTo(MethodType newType) {
if (!canConvert(returnType(), newType.returnType()))
return false;
int argc = parameterCount();
if (argc != newType.parameterCount())
return false;
for (int i = 0; i < argc; i++) {
if (!canConvert(newType.parameterType(i), parameterType(i)))
return false;
}
return true;
}
private static boolean canConvert(Class<?> src, Class<?> dst) {
if (src == dst || dst == void.class) return true;
if (src.isPrimitive() && dst.isPrimitive()) {
if (!Wrapper.forPrimitiveType(dst)
.isConvertibleFrom(Wrapper.forPrimitiveType(src)))
return false;
}
return true;
}
/// Queries which have to do with the bytecode architecture /// Queries which have to do with the bytecode architecture
/** Reports the number of JVM stack slots required to invoke a method /** Reports the number of JVM stack slots required to invoke a method
......
...@@ -46,6 +46,7 @@ class MethodTypeForm { ...@@ -46,6 +46,7 @@ class MethodTypeForm {
final long argCounts; // packed slot & value counts final long argCounts; // packed slot & value counts
final long primCounts; // packed prim & double counts final long primCounts; // packed prim & double counts
final int vmslots; // total number of parameter slots final int vmslots; // total number of parameter slots
private Object vmlayout; // vm-specific information for calls
final MethodType erasedType; // the canonical erasure final MethodType erasedType; // the canonical erasure
/*lazy*/ MethodType primsAsBoxes; // replace prims by wrappers /*lazy*/ MethodType primsAsBoxes; // replace prims by wrappers
......
...@@ -66,6 +66,10 @@ class SpreadGeneric { ...@@ -66,6 +66,10 @@ class SpreadGeneric {
this.entryPoint = ep[0]; this.entryPoint = ep[0];
} }
static {
assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this class is deprecated
}
/** From targetType remove the last spreadCount arguments, and instead /** From targetType remove the last spreadCount arguments, and instead
* append a simple Object argument. * append a simple Object argument.
*/ */
......
...@@ -96,7 +96,7 @@ class ToGeneric { ...@@ -96,7 +96,7 @@ class ToGeneric {
ToGeneric va2 = ToGeneric.of(primsAtEnd); ToGeneric va2 = ToGeneric.of(primsAtEnd);
this.adapter = va2.adapter; this.adapter = va2.adapter;
if (true) throw new UnsupportedOperationException("NYI: primitive parameters must follow references; entryType = "+entryType); if (true) throw new UnsupportedOperationException("NYI: primitive parameters must follow references; entryType = "+entryType);
this.entryPoint = MethodHandleImpl.convertArguments( this.entryPoint = MethodHandleImpl.permuteArguments(
va2.entryPoint, primsAtEnd, entryType, primsAtEndOrder); va2.entryPoint, primsAtEnd, entryType, primsAtEndOrder);
// example: for entryType of (int,Object,Object), the reordered // example: for entryType of (int,Object,Object), the reordered
// type is (Object,Object,int) and the order is {1,2,0}, // type is (Object,Object,int) and the order is {1,2,0},
...@@ -128,7 +128,7 @@ class ToGeneric { ...@@ -128,7 +128,7 @@ class ToGeneric {
assert(eptWithInts.parameterType(i) == int.class); assert(eptWithInts.parameterType(i) == int.class);
MethodType nextType = midType.changeParameterType(i, int.class); MethodType nextType = midType.changeParameterType(i, int.class);
rawEntryPoint = MethodHandleImpl.convertArguments( rawEntryPoint = MethodHandleImpl.convertArguments(
rawEntryPoint, nextType, midType, null); rawEntryPoint, nextType, midType, 0);
midType = nextType; midType = nextType;
} }
} }
...@@ -152,6 +152,10 @@ class ToGeneric { ...@@ -152,6 +152,10 @@ class ToGeneric {
this.invoker = makeRawArgumentFilter(invoker0, rawEntryTypeInit, entryType); this.invoker = makeRawArgumentFilter(invoker0, rawEntryTypeInit, entryType);
} }
static {
assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this class is deprecated
}
/** A generic argument list will be created by a call of type 'raw'. /** A generic argument list will be created by a call of type 'raw'.
* The values need to be reboxed for to match 'cooked'. * The values need to be reboxed for to match 'cooked'.
* Do this on the fly. * Do this on the fly.
...@@ -171,7 +175,7 @@ class ToGeneric { ...@@ -171,7 +175,7 @@ class ToGeneric {
invoker.type().generic(), invoker, 0, MethodHandle.class); invoker.type().generic(), invoker, 0, MethodHandle.class);
if (filteredInvoker == null) throw new UnsupportedOperationException("NYI"); if (filteredInvoker == null) throw new UnsupportedOperationException("NYI");
} }
MethodHandle reboxer = ValueConversions.rebox(dst, false); MethodHandle reboxer = ValueConversions.rebox(dst);
filteredInvoker = FilterGeneric.makeArgumentFilter(1+i, reboxer, filteredInvoker); filteredInvoker = FilterGeneric.makeArgumentFilter(1+i, reboxer, filteredInvoker);
if (filteredInvoker == null) throw new InternalError(); if (filteredInvoker == null) throw new InternalError();
} }
...@@ -199,13 +203,13 @@ class ToGeneric { ...@@ -199,13 +203,13 @@ class ToGeneric {
assert(!rret.isPrimitive()); assert(!rret.isPrimitive());
if (rret == Object.class && !mustCast) if (rret == Object.class && !mustCast)
return null; return null;
return ValueConversions.cast(tret, false); return ValueConversions.cast(tret);
} else if (tret == rret) { } else if (tret == rret) {
return ValueConversions.unbox(tret, false); return ValueConversions.unbox(tret);
} else { } else {
assert(rret.isPrimitive()); assert(rret.isPrimitive());
assert(tret == double.class ? rret == long.class : rret == int.class); assert(tret == double.class ? rret == long.class : rret == int.class);
return ValueConversions.unboxRaw(tret, false); return ValueConversions.unboxRaw(tret);
} }
} }
...@@ -311,7 +315,7 @@ class ToGeneric { ...@@ -311,7 +315,7 @@ class ToGeneric {
} }
static Adapter buildAdapterFromBytecodes(MethodType entryPointType) { static Adapter buildAdapterFromBytecodes(MethodType entryPointType) {
throw new UnsupportedOperationException("NYI"); throw new UnsupportedOperationException("NYI: "+entryPointType);
} }
/** /**
......
...@@ -54,9 +54,15 @@ public class VerifyType { ...@@ -54,9 +54,15 @@ public class VerifyType {
if (dst == void.class) return true; // drop any return value if (dst == void.class) return true; // drop any return value
if (isNullType(src)) return !dst.isPrimitive(); if (isNullType(src)) return !dst.isPrimitive();
if (!src.isPrimitive()) return dst.isAssignableFrom(src); if (!src.isPrimitive()) return dst.isAssignableFrom(src);
if (!dst.isPrimitive()) return false;
// Verifier allows an int to carry byte, short, char, or even boolean: // Verifier allows an int to carry byte, short, char, or even boolean:
if (dst == int.class) return Wrapper.forPrimitiveType(src).isSubwordOrInt(); Wrapper sw = Wrapper.forPrimitiveType(src);
return false; if (dst == int.class) return sw.isSubwordOrInt();
Wrapper dw = Wrapper.forPrimitiveType(dst);
if (!sw.isSubwordOrInt()) return false;
if (!dw.isSubwordOrInt()) return false;
if (!dw.isSigned() && sw.isSigned()) return false;
return dw.bitWidth() > sw.bitWidth();
} }
/** /**
...@@ -155,6 +161,7 @@ public class VerifyType { ...@@ -155,6 +161,7 @@ public class VerifyType {
return -1; // truncation may be required return -1; // truncation may be required
if (!dw.isSigned() && sw.isSigned()) if (!dw.isSigned() && sw.isSigned())
return -1; // sign elimination may be required return -1; // sign elimination may be required
return 1;
} }
if (src == float.class || dst == float.class) { if (src == float.class || dst == float.class) {
if (src == double.class || dst == double.class) if (src == double.class || dst == double.class)
......
...@@ -26,37 +26,47 @@ ...@@ -26,37 +26,47 @@
package sun.invoke.util; package sun.invoke.util;
public enum Wrapper { public enum Wrapper {
BOOLEAN(Boolean.class, boolean.class, 'Z', (Boolean)false, Format.unsigned(1)), BOOLEAN(Boolean.class, boolean.class, 'Z', (Boolean)false, new boolean[0], Format.unsigned(1)),
// These must be in the order defined for widening primitive conversions in JLS 5.1.2 // These must be in the order defined for widening primitive conversions in JLS 5.1.2
BYTE(Byte.class, byte.class, 'B', (Byte)(byte)0, Format.signed(8)), BYTE(Byte.class, byte.class, 'B', (Byte)(byte)0, new byte[0], Format.signed(8)),
SHORT(Short.class, short.class, 'S', (Short)(short)0, Format.signed(16)), SHORT(Short.class, short.class, 'S', (Short)(short)0, new short[0], Format.signed(16)),
CHAR(Character.class, char.class, 'C', (Character)(char)0, Format.unsigned(16)), CHAR(Character.class, char.class, 'C', (Character)(char)0, new char[0], Format.unsigned(16)),
INT(Integer.class, int.class, 'I', (Integer)(int)0, Format.signed(32)), INT(Integer.class, int.class, 'I', (Integer)(int)0, new int[0], Format.signed(32)),
LONG(Long.class, long.class, 'J', (Long)(long)0, Format.signed(64)), LONG(Long.class, long.class, 'J', (Long)(long)0, new long[0], Format.signed(64)),
FLOAT(Float.class, float.class, 'F', (Float)(float)0, Format.floating(32)), FLOAT(Float.class, float.class, 'F', (Float)(float)0, new float[0], Format.floating(32)),
DOUBLE(Double.class, double.class, 'D', (Double)(double)0, Format.floating(64)), DOUBLE(Double.class, double.class, 'D', (Double)(double)0, new double[0], Format.floating(64)),
//NULL(Null.class, null.class, 'N', null, Format.other(1)), //NULL(Null.class, null.class, 'N', null, null, Format.other(1)),
OBJECT(Object.class, Object.class, 'L', null, Format.other(1)), OBJECT(Object.class, Object.class, 'L', null, new Object[0], Format.other(1)),
// VOID must be the last type, since it is "assignable" from any other type: // VOID must be the last type, since it is "assignable" from any other type:
VOID(Void.class, void.class, 'V', null, Format.other(0)), VOID(Void.class, void.class, 'V', null, null, Format.other(0)),
; ;
private final Class<?> wrapperType; private final Class<?> wrapperType;
private final Class<?> primitiveType; private final Class<?> primitiveType;
private final char basicTypeChar; private final char basicTypeChar;
private final Object zero; private final Object zero;
private final Object emptyArray;
private final int format; private final int format;
private final String simpleName; private final String simpleName;
private Wrapper(Class<?> wtype, Class<?> ptype, char tchar, Object zero, int format) { private Wrapper(Class<?> wtype, Class<?> ptype, char tchar, Object zero, Object emptyArray, int format) {
this.wrapperType = wtype; this.wrapperType = wtype;
this.primitiveType = ptype; this.primitiveType = ptype;
this.basicTypeChar = tchar; this.basicTypeChar = tchar;
this.zero = zero; this.zero = zero;
this.emptyArray = emptyArray;
this.format = format; this.format = format;
this.simpleName = wtype.getSimpleName(); this.simpleName = wtype.getSimpleName();
} }
/** For debugging, give the details of this wrapper. */
public String detailString() {
return simpleName+
java.util.Arrays.asList(wrapperType, primitiveType,
basicTypeChar, zero,
"0x"+Integer.toHexString(format));
}
private static abstract class Format { private static abstract class Format {
static final int SLOT_SHIFT = 0, SIZE_SHIFT = 2, KIND_SHIFT = 12; static final int SLOT_SHIFT = 0, SIZE_SHIFT = 2, KIND_SHIFT = 12;
static final int static final int
...@@ -114,16 +124,18 @@ public enum Wrapper { ...@@ -114,16 +124,18 @@ public enum Wrapper {
public boolean isUnsigned() { return format >= Format.BOOLEAN && format < Format.FLOAT; } public boolean isUnsigned() { return format >= Format.BOOLEAN && format < Format.FLOAT; }
/** Is the wrapped type either float or double? */ /** Is the wrapped type either float or double? */
public boolean isFloating() { return format >= Format.FLOAT; } public boolean isFloating() { return format >= Format.FLOAT; }
/** Is the wrapped type either void or a reference? */
public boolean isOther() { return (format & ~Format.SLOT_MASK) == 0; }
/** Does the JVM verifier allow a variable of this wrapper's /** Does the JLS 5.1.2 allow a variable of this wrapper's
* primitive type to be assigned from a value of the given wrapper's primitive type? * primitive type to be assigned from a value of the given wrapper's primitive type?
* Cases: * Cases:
* <ul> * <ul>
* <li>unboxing followed by widening primitive conversion * <li>unboxing followed by widening primitive conversion
* <li>any type converted to {@code void} * <li>any type converted to {@code void} (i.e., dropping a method call's value)
* <li>boxing conversion followed by widening reference conversion to {@code Object} * <li>boxing conversion followed by widening reference conversion to {@code Object}
* <li>conversion of {@code boolean} to any type
* </ul> * </ul>
* These are the cases allowed by MethodHandle.asType and convertArguments.
*/ */
public boolean isConvertibleFrom(Wrapper source) { public boolean isConvertibleFrom(Wrapper source) {
if (this == source) return true; if (this == source) return true;
...@@ -131,13 +143,75 @@ public enum Wrapper { ...@@ -131,13 +143,75 @@ public enum Wrapper {
// At best, this is a narrowing conversion. // At best, this is a narrowing conversion.
return false; return false;
} }
if ((this.format ^ source.format) == (Format.SHORT ^ Format.CHAR)) { // All conversions are allowed in the enum order between floats and signed ints.
assert (this == SHORT && source == CHAR) || (this == CHAR && source == SHORT); // First detect non-signed non-float types (boolean, char, Object, void).
boolean floatOrSigned = (((this.format & source.format) & Format.SIGNED) != 0);
if (!floatOrSigned) {
if (this.isOther()) return true;
// can convert char to int or wider, but nothing else
if (source.format == Format.CHAR) return true;
// no other conversions are classified as widening
return false; return false;
} }
// All signed and float conversions in the enum order are widening.
assert(this.isFloating() || this.isSigned());
assert(source.isFloating() || source.isSigned());
return true; return true;
} }
static { assert(checkConvertibleFrom()); }
private static boolean checkConvertibleFrom() {
// Check the matrix for correct classification of widening conversions.
for (Wrapper w : values()) {
assert(w.isConvertibleFrom(w));
assert(VOID.isConvertibleFrom(w));
if (w != VOID) {
assert(OBJECT.isConvertibleFrom(w));
assert(!w.isConvertibleFrom(VOID));
}
// check relations with unsigned integral types:
if (w != CHAR) {
assert(!CHAR.isConvertibleFrom(w));
if (!w.isConvertibleFrom(INT))
assert(!w.isConvertibleFrom(CHAR));
}
if (w != BOOLEAN) {
assert(!BOOLEAN.isConvertibleFrom(w));
if (w != VOID && w != OBJECT)
assert(!w.isConvertibleFrom(BOOLEAN));
}
// check relations with signed integral types:
if (w.isSigned()) {
for (Wrapper x : values()) {
if (w == x) continue;
if (x.isFloating())
assert(!w.isConvertibleFrom(x));
else if (x.isSigned()) {
if (w.compareTo(x) < 0)
assert(!w.isConvertibleFrom(x));
else
assert(w.isConvertibleFrom(x));
}
}
}
// check relations with floating types:
if (w.isFloating()) {
for (Wrapper x : values()) {
if (w == x) continue;
if (x.isSigned())
assert(w.isConvertibleFrom(x));
else if (x.isFloating()) {
if (w.compareTo(x) < 0)
assert(!w.isConvertibleFrom(x));
else
assert(w.isConvertibleFrom(x));
}
}
}
}
return true; // i.e., assert(true)
}
/** Produce a zero value for the given wrapper type. /** Produce a zero value for the given wrapper type.
* This will be a numeric zero for a number or character, * This will be a numeric zero for a number or character,
* false for a boolean, and null for a reference or void. * false for a boolean, and null for a reference or void.
...@@ -549,7 +623,7 @@ public enum Wrapper { ...@@ -549,7 +623,7 @@ public enum Wrapper {
} }
private static boolean boolValue(long bits) { private static boolean boolValue(long bits) {
//bits &= 1; // simple 31-bit zero extension bits &= 1; // simple 31-bit zero extension
return (bits != 0); return (bits != 0);
} }
...@@ -559,4 +633,31 @@ public enum Wrapper { ...@@ -559,4 +633,31 @@ public enum Wrapper {
private static RuntimeException newIllegalArgumentException(String message) { private static RuntimeException newIllegalArgumentException(String message) {
return new IllegalArgumentException(message); return new IllegalArgumentException(message);
} }
// primitive array support
public Object makeArray(int len) {
return java.lang.reflect.Array.newInstance(primitiveType, len);
}
public Class<?> arrayType() {
return emptyArray.getClass();
}
public void copyArrayUnboxing(Object[] values, int vpos, Object a, int apos, int length) {
if (a.getClass() != arrayType())
arrayType().cast(a); // throw NPE or CCE if bad type
for (int i = 0; i < length; i++) {
Object value = values[i+vpos];
value = convert(value, primitiveType);
java.lang.reflect.Array.set(a, i+apos, value);
}
}
public void copyArrayBoxing(Object a, int apos, Object[] values, int vpos, int length) {
if (a.getClass() != arrayType())
arrayType().cast(a); // throw NPE or CCE if bad type
for (int i = 0; i < length; i++) {
Object value = java.lang.reflect.Array.get(a, i+apos);
//Already done: value = convert(value, primitiveType);
assert(value.getClass() == wrapperType);
values[i+vpos] = value;
}
}
} }
此差异已折叠。
...@@ -53,6 +53,10 @@ public class InvokeGenericTest { ...@@ -53,6 +53,10 @@ public class InvokeGenericTest {
if (vstr != null) verbosity = Integer.parseInt(vstr); if (vstr != null) verbosity = Integer.parseInt(vstr);
} }
public static void main(String... av) throws Throwable {
new InvokeGenericTest().testFirst();
}
@Test @Test
public void testFirst() throws Throwable { public void testFirst() throws Throwable {
verbosity += 9; try { verbosity += 9; try {
...@@ -103,7 +107,7 @@ public class InvokeGenericTest { ...@@ -103,7 +107,7 @@ public class InvokeGenericTest {
void startTest(String name) { void startTest(String name) {
if (testName != null) printCounts(); if (testName != null) printCounts();
if (verbosity >= 1) if (verbosity >= 1)
System.out.println(name); System.out.println("["+name+"]");
posTests = negTests = 0; posTests = negTests = 0;
testName = name; testName = name;
} }
...@@ -355,6 +359,18 @@ public class InvokeGenericTest { ...@@ -355,6 +359,18 @@ public class InvokeGenericTest {
assertEquals(Arrays.asList(args), res); assertEquals(Arrays.asList(args), res);
} }
@Test
public void testSimplePrims() throws Throwable {
startTest("testSimplePrims");
countTest();
int[] args = { 1, 2 };
MethodHandle mh = callable(Object.class, Object.class);
Object res; List resl;
res = resl = (List) mh.invoke(args[0], args[1]);
//System.out.println(res);
assertEquals(Arrays.toString(args), res.toString());
}
@Test @Test
public void testAlternateName() throws Throwable { public void testAlternateName() throws Throwable {
startTest("testAlternateName"); startTest("testAlternateName");
...@@ -415,9 +431,9 @@ public class InvokeGenericTest { ...@@ -415,9 +431,9 @@ public class InvokeGenericTest {
} catch (WrongMethodTypeException ex) { } catch (WrongMethodTypeException ex) {
return; return;
} catch (Exception ex) { } catch (Exception ex) {
throw new RuntimeException("wrong exception calling "+target+target.type()+" on "+Arrays.asList(args)+" : "+ex); throw new RuntimeException("wrong exception calling "+target+" on "+Arrays.asList(args), ex);
} }
throw new RuntimeException("bad success calling "+target+target.type()+" on "+Arrays.asList(args)); throw new RuntimeException("bad success calling "+target+" on "+Arrays.asList(args));
} }
/** Make a list of all combinations of the given types, with the given arities. /** Make a list of all combinations of the given types, with the given arities.
......
...@@ -105,24 +105,6 @@ public class MethodHandlesTest { ...@@ -105,24 +105,6 @@ public class MethodHandlesTest {
public MethodHandlesTest() { public MethodHandlesTest() {
} }
@Before
public void checkImplementedPlatform() {
boolean platformOK = false;
Properties properties = System.getProperties();
String vers = properties.getProperty("java.vm.version");
String name = properties.getProperty("java.vm.name");
String arch = properties.getProperty("os.arch");
if ((arch.equals("amd64") || arch.equals("i386") || arch.equals("x86") ||
arch.equals("sparc") || arch.equals("sparcv9")) &&
(name.contains("Client") || name.contains("Server"))
) {
platformOK = true;
} else {
System.err.println("Skipping tests for unsupported platform: "+Arrays.asList(vers, name, arch));
}
assumeTrue(platformOK);
}
String testName; String testName;
static int allPosTests, allNegTests; static int allPosTests, allNegTests;
int posTests, negTests; int posTests, negTests;
...@@ -331,6 +313,9 @@ public class MethodHandlesTest { ...@@ -331,6 +313,9 @@ public class MethodHandlesTest {
static MethodHandle varargsArray(int arity) { static MethodHandle varargsArray(int arity) {
return ValueConversions.varargsArray(arity); return ValueConversions.varargsArray(arity);
} }
static MethodHandle varargsArray(Class<?> arrayType, int arity) {
return ValueConversions.varargsArray(arrayType, arity);
}
/** Variation of varargsList, but with the given rtype. */ /** Variation of varargsList, but with the given rtype. */
static MethodHandle varargsList(int arity, Class<?> rtype) { static MethodHandle varargsList(int arity, Class<?> rtype) {
MethodHandle list = varargsList(arity); MethodHandle list = varargsList(arity);
...@@ -865,7 +850,7 @@ public class MethodHandlesTest { ...@@ -865,7 +850,7 @@ public class MethodHandlesTest {
Class<?> type = (Class<?>) t[1]; Class<?> type = (Class<?>) t[1];
Object value; Object value;
Field field; Field field;
try { try {
field = HasFields.class.getDeclaredField(name); field = HasFields.class.getDeclaredField(name);
} catch (Exception ex) { } catch (Exception ex) {
throw new InternalError("no field HasFields."+name); throw new InternalError("no field HasFields."+name);
...@@ -1144,16 +1129,9 @@ public class MethodHandlesTest { ...@@ -1144,16 +1129,9 @@ public class MethodHandlesTest {
: MethodHandles.arrayElementSetter(arrayType); : MethodHandles.arrayElementSetter(arrayType);
assertSame(mh.type(), expType); assertSame(mh.type(), expType);
if (elemType != int.class && elemType != boolean.class) { if (elemType != int.class && elemType != boolean.class) {
MethodType gtype; // FIXME: change Integer.class and (Integer) below to int.class and (int) below.
if (true) { // FIXME: remove this path (and remove <void> below in the mh.invokes) MethodType gtype = mh.type().generic().changeParameterType(1, Integer.class);
gtype = mh.type().changeParameterType(0, Object.class); if (testSetter) gtype = gtype.changeReturnType(void.class);
if (testSetter)
gtype = gtype.changeParameterType(2, Object.class);
else
gtype = gtype.changeReturnType(Object.class);
} else
// FIXME: This simpler path hits a bug in convertArguments => ToGeneric
gtype = mh.type().generic().changeParameterType(1, int.class);
mh = MethodHandles.convertArguments(mh, gtype); mh = MethodHandles.convertArguments(mh, gtype);
} }
Object sawValue, expValue; Object sawValue, expValue;
...@@ -1169,7 +1147,7 @@ public class MethodHandlesTest { ...@@ -1169,7 +1147,7 @@ public class MethodHandlesTest {
else if (elemType == boolean.class) else if (elemType == boolean.class)
mh.invokeExact((boolean[]) array, i, (boolean)random); mh.invokeExact((boolean[]) array, i, (boolean)random);
else else
mh.invokeExact(array, i, random); mh.invokeExact(array, (Integer)i, random);
assertEquals(model, array2list(array)); assertEquals(model, array2list(array));
} else { } else {
Array.set(array, i, random); Array.set(array, i, random);
...@@ -1189,7 +1167,7 @@ public class MethodHandlesTest { ...@@ -1189,7 +1167,7 @@ public class MethodHandlesTest {
else if (elemType == boolean.class) else if (elemType == boolean.class)
sawValue = (boolean) mh.invokeExact((boolean[]) array, i); sawValue = (boolean) mh.invokeExact((boolean[]) array, i);
else else
sawValue = mh.invokeExact(array, i); sawValue = mh.invokeExact(array, (Integer)i);
assertEquals(sawValue, expValue); assertEquals(sawValue, expValue);
assertEquals(model, array2list(array)); assertEquals(model, array2list(array));
} }
...@@ -1341,21 +1319,15 @@ public class MethodHandlesTest { ...@@ -1341,21 +1319,15 @@ public class MethodHandlesTest {
int numcases = 1; int numcases = 1;
for (int outargs = 0; outargs <= max; outargs++) { for (int outargs = 0; outargs <= max; outargs++) {
if (outargs - inargs >= MAX_ARG_INCREASE) continue; if (outargs - inargs >= MAX_ARG_INCREASE) continue;
int[] reorder = new int[outargs];
int casStep = dilution + 1; int casStep = dilution + 1;
// Avoid some common factors: // Avoid some common factors:
while ((casStep > 2 && casStep % 2 == 0 && inargs % 2 == 0) || while ((casStep > 2 && casStep % 2 == 0 && inargs % 2 == 0) ||
(casStep > 3 && casStep % 3 == 0 && inargs % 3 == 0)) (casStep > 3 && casStep % 3 == 0 && inargs % 3 == 0))
casStep++; casStep++;
for (int cas = 0; cas < numcases; cas += casStep) { testPermuteArguments(args, types, outargs, numcases, casStep);
for (int i = 0, c = cas; i < outargs; i++) {
reorder[i] = c % inargs;
c /= inargs;
}
testPermuteArguments(args, types, reorder);
}
numcases *= inargs; numcases *= inargs;
if (dilution > 10 && outargs >= 4) { if (dilution > 10 && outargs >= 4) {
int[] reorder = new int[outargs];
// Do some special patterns, which we probably missed. // Do some special patterns, which we probably missed.
// Replication of a single argument or argument pair. // Replication of a single argument or argument pair.
for (int i = 0; i < inargs; i++) { for (int i = 0; i < inargs; i++) {
...@@ -1383,6 +1355,19 @@ public class MethodHandlesTest { ...@@ -1383,6 +1355,19 @@ public class MethodHandlesTest {
} }
} }
public void testPermuteArguments(Object[] args, Class<?>[] types,
int outargs, int numcases, int casStep) throws Throwable {
int inargs = args.length;
int[] reorder = new int[outargs];
for (int cas = 0; cas < numcases; cas += casStep) {
for (int i = 0, c = cas; i < outargs; i++) {
reorder[i] = c % inargs;
c /= inargs;
}
testPermuteArguments(args, types, reorder);
}
}
static int[] reverse(int[] reorder) { static int[] reverse(int[] reorder) {
reorder = reorder.clone(); reorder = reorder.clone();
for (int i = 0, imax = reorder.length / 2; i < imax; i++) { for (int i = 0, imax = reorder.length / 2; i < imax; i++) {
...@@ -1433,6 +1418,12 @@ public class MethodHandlesTest { ...@@ -1433,6 +1418,12 @@ public class MethodHandlesTest {
MethodHandle newTarget = MethodHandles.permuteArguments(target, inType, reorder); MethodHandle newTarget = MethodHandles.permuteArguments(target, inType, reorder);
Object result = newTarget.invokeWithArguments(args); Object result = newTarget.invokeWithArguments(args);
Object expected = Arrays.asList(permArgs); Object expected = Arrays.asList(permArgs);
if (!expected.equals(result)) {
System.out.println("*** failed permuteArguments "+Arrays.toString(reorder)+" types="+Arrays.asList(types));
System.out.println("in args: "+Arrays.asList(args));
System.out.println("out args: "+expected);
System.out.println("bad args: "+result);
}
assertEquals(expected, result); assertEquals(expected, result);
} }
...@@ -1456,26 +1447,27 @@ public class MethodHandlesTest { ...@@ -1456,26 +1447,27 @@ public class MethodHandlesTest {
} }
public void testSpreadArguments(Class<?> argType, int pos, int nargs) throws Throwable { public void testSpreadArguments(Class<?> argType, int pos, int nargs) throws Throwable {
countTest(); countTest();
MethodHandle target = varargsArray(nargs); Class<?> arrayType = java.lang.reflect.Array.newInstance(argType, 0).getClass();
MethodHandle target2 = changeArgTypes(target, argType); MethodHandle target2 = varargsArray(arrayType, nargs);
MethodHandle target = target2.asType(target2.type().generic());
if (verbosity >= 3) if (verbosity >= 3)
System.out.println("spread into "+target2+" ["+pos+".."+nargs+"]"); System.out.println("spread into "+target2+" ["+pos+".."+nargs+"]");
Object[] args = randomArgs(target2.type().parameterArray()); Object[] args = randomArgs(target2.type().parameterArray());
// make sure the target does what we think it does: // make sure the target does what we think it does:
if (pos == 0 && nargs < 5) { if (pos == 0 && nargs < 5 && !argType.isPrimitive()) {
Object[] check = (Object[]) target.invokeWithArguments(args); Object[] check = (Object[]) (Object) target.invokeWithArguments(args);
assertArrayEquals(args, check); assertArrayEquals(args, check);
switch (nargs) { switch (nargs) {
case 0: case 0:
check = (Object[]) target.invokeExact(); check = (Object[]) (Object) target.invokeExact();
assertArrayEquals(args, check); assertArrayEquals(args, check);
break; break;
case 1: case 1:
check = (Object[]) target.invokeExact(args[0]); check = (Object[]) (Object) target.invokeExact(args[0]);
assertArrayEquals(args, check); assertArrayEquals(args, check);
break; break;
case 2: case 2:
check = (Object[]) target.invokeExact(args[0], args[1]); check = (Object[]) (Object) target.invokeExact(args[0], args[1]);
assertArrayEquals(args, check); assertArrayEquals(args, check);
break; break;
} }
...@@ -1483,23 +1475,50 @@ public class MethodHandlesTest { ...@@ -1483,23 +1475,50 @@ public class MethodHandlesTest {
List<Class<?>> newParams = new ArrayList<Class<?>>(target2.type().parameterList()); List<Class<?>> newParams = new ArrayList<Class<?>>(target2.type().parameterList());
{ // modify newParams in place { // modify newParams in place
List<Class<?>> spreadParams = newParams.subList(pos, nargs); List<Class<?>> spreadParams = newParams.subList(pos, nargs);
spreadParams.clear(); spreadParams.add(Object[].class); spreadParams.clear(); spreadParams.add(arrayType);
} }
MethodType newType = MethodType.methodType(Object.class, newParams); MethodType newType = MethodType.methodType(arrayType, newParams);
MethodHandle result = target2.asSpreader(Object[].class, nargs-pos).asType(newType); MethodHandle result = target2.asSpreader(arrayType, nargs-pos);
Object[] returnValue; assert(result.type() == newType) : Arrays.asList(result, newType);
result = result.asType(newType.generic());
Object returnValue;
if (pos == 0) { if (pos == 0) {
// In the following line, the first cast implies Object args2 = ValueConversions.changeArrayType(arrayType, Arrays.copyOfRange(args, pos, args.length));
// normal Object return value for the MH call (Object[])->Object, returnValue = result.invokeExact(args2);
// while the second cast dynamically converts to an Object array.
// Such a double cast is typical of MH.invokeExact.
returnValue = (Object[]) (Object) result.invokeExact(args);
} else { } else {
Object[] args1 = Arrays.copyOfRange(args, 0, pos+1); Object[] args1 = Arrays.copyOfRange(args, 0, pos+1);
args1[pos] = Arrays.copyOfRange(args, pos, args.length); args1[pos] = ValueConversions.changeArrayType(arrayType, Arrays.copyOfRange(args, pos, args.length));
returnValue = (Object[]) result.invokeWithArguments(args1); returnValue = result.invokeWithArguments(args1);
}
String argstr = Arrays.toString(args);
if (!argType.isPrimitive()) {
Object[] rv = (Object[]) returnValue;
String rvs = Arrays.toString(rv);
if (!Arrays.equals(args, rv)) {
System.out.println("method: "+result);
System.out.println("expected: "+argstr);
System.out.println("returned: "+rvs);
assertArrayEquals(args, rv);
}
} else if (argType == int.class) {
String rvs = Arrays.toString((int[]) returnValue);
if (!argstr.equals(rvs)) {
System.out.println("method: "+result);
System.out.println("expected: "+argstr);
System.out.println("returned: "+rvs);
assertEquals(argstr, rvs);
}
} else if (argType == long.class) {
String rvs = Arrays.toString((long[]) returnValue);
if (!argstr.equals(rvs)) {
System.out.println("method: "+result);
System.out.println("expected: "+argstr);
System.out.println("returned: "+rvs);
assertEquals(argstr, rvs);
}
} else {
// cannot test...
} }
assertArrayEquals(args, returnValue);
} }
@Test @Test
...@@ -2130,15 +2149,12 @@ public class MethodHandlesTest { ...@@ -2130,15 +2149,12 @@ public class MethodHandlesTest {
Object z = surprise.invokeExact(x); Object z = surprise.invokeExact(x);
System.out.println("Failed to throw; got z="+z); System.out.println("Failed to throw; got z="+z);
assertTrue(false); assertTrue(false);
} catch (Exception ex) { } catch (ClassCastException ex) {
if (verbosity > 2) if (verbosity > 2)
System.out.println("caught "+ex); System.out.println("caught "+ex);
if (verbosity > 3) if (verbosity > 3)
ex.printStackTrace(); ex.printStackTrace();
assertTrue(ex instanceof ClassCastException assertTrue(true); // all is well
// FIXME: accept only one of the two for any given unit test
|| ex instanceof WrongMethodTypeException
);
} }
} }
...@@ -2328,6 +2344,34 @@ class ValueConversions { ...@@ -2328,6 +2344,34 @@ class ValueConversions {
// else need to spin bytecode or do something else fancy // else need to spin bytecode or do something else fancy
throw new UnsupportedOperationException("NYI: cannot form a varargs array of length "+nargs); throw new UnsupportedOperationException("NYI: cannot form a varargs array of length "+nargs);
} }
public static MethodHandle varargsArray(Class<?> arrayType, int nargs) {
Class<?> elemType = arrayType.getComponentType();
MethodType vaType = MethodType.methodType(arrayType, Collections.<Class<?>>nCopies(nargs, elemType));
MethodHandle mh = varargsArray(nargs);
if (arrayType != Object[].class)
mh = MethodHandles.filterReturnValue(mh, CHANGE_ARRAY_TYPE.bindTo(arrayType));
return mh.asType(vaType);
}
static Object changeArrayType(Class<?> arrayType, Object[] a) {
Class<?> elemType = arrayType.getComponentType();
if (!elemType.isPrimitive())
return Arrays.copyOf(a, a.length, arrayType.asSubclass(Object[].class));
Object b = java.lang.reflect.Array.newInstance(elemType, a.length);
for (int i = 0; i < a.length; i++)
java.lang.reflect.Array.set(b, i, a[i]);
return b;
}
private static final MethodHandle CHANGE_ARRAY_TYPE;
static {
try {
CHANGE_ARRAY_TYPE = IMPL_LOOKUP.findStatic(ValueConversions.class, "changeArrayType",
MethodType.methodType(Object.class, Class.class, Object[].class));
} catch (NoSuchMethodException | IllegalAccessException ex) {
Error err = new InternalError("uncaught exception");
err.initCause(ex);
throw err;
}
}
private static final List<Object> NO_ARGS_LIST = Arrays.asList(NO_ARGS_ARRAY); private static final List<Object> NO_ARGS_LIST = Arrays.asList(NO_ARGS_ARRAY);
private static List<Object> makeList(Object... args) { return Arrays.asList(args); } private static List<Object> makeList(Object... args) { return Arrays.asList(args); }
......
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册