提交 4b939f2a 编写于 作者: S schien

Merge

......@@ -273,9 +273,9 @@ public class CallSite {
Object binding;
info = maybeReBox(info);
if (info == null) {
binding = bootstrapMethod.invokeGeneric(caller, name, type);
binding = bootstrapMethod.invoke(caller, name, type);
} else if (!info.getClass().isArray()) {
binding = bootstrapMethod.invokeGeneric(caller, name, type, info);
binding = bootstrapMethod.invoke(caller, name, type, info);
} else {
Object[] argv = (Object[]) info;
maybeReBoxElements(argv);
......@@ -283,10 +283,10 @@ public class CallSite {
throw new BootstrapMethodError("too many bootstrap method arguments");
MethodType bsmType = bootstrapMethod.type();
if (bsmType.parameterCount() == 4 && bsmType.parameterType(3) == Object[].class)
binding = bootstrapMethod.invokeGeneric(caller, name, type, argv);
binding = bootstrapMethod.invoke(caller, name, type, argv);
else
binding = MethodHandles.spreadInvoker(bsmType, 3)
.invokeGeneric(bootstrapMethod, caller, name, type, argv);
.invoke(bootstrapMethod, caller, name, type, argv);
}
//System.out.println("BSM for "+name+type+" => "+binding);
if (binding instanceof CallSite) {
......
......@@ -61,6 +61,10 @@ class FilterGeneric {
return ad;
}
static {
assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this class is deprecated
}
Adapter makeInstance(Kind kind, int pos, MethodHandle filter, MethodHandle target) {
Adapter ad = getAdapter(kind, pos);
return ad.makeInstance(ad.prototypeEntryPoint(), filter, target);
......
......@@ -67,6 +67,10 @@ class FilterOneArgument extends BoundMethodHandle {
this.target = target;
}
static {
assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this class is deprecated
}
public static MethodHandle make(MethodHandle filter, MethodHandle target) {
if (filter == null) return target;
if (target == null) return filter;
......
......@@ -98,6 +98,10 @@ class FromGeneric {
this.unboxingInvoker = computeUnboxingInvoker(targetType, internalType0);
}
static {
assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this class is deprecated
}
/**
* The typed target will be called according to targetType.
* The adapter code will in fact see the raw result from internalType,
......@@ -112,10 +116,10 @@ class FromGeneric {
assert(iret == Object.class);
return ValueConversions.identity();
} else if (wrap.primitiveType() == iret) {
return ValueConversions.box(wrap, false);
return ValueConversions.box(wrap);
} else {
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 {
MethodType fixArgsType = internalType.changeReturnType(targetType.returnType());
MethodHandle fixArgs = MethodHandleImpl.convertArguments(
invoker, Invokers.invokerType(fixArgsType),
invoker.type(), null);
invoker.type(), 0);
if (fixArgs == null)
throw new InternalError("bad fixArgs");
// reinterpret the calling sequence as raw:
......@@ -160,7 +164,6 @@ class FromGeneric {
/** Build an adapter of the given generic type, which invokes typedTarget
* on the incoming arguments, after unboxing as necessary.
* The return value is boxed if necessary.
* @param genericType the required type of the result
* @param typedTarget the target
* @return an adapter method handle
*/
......@@ -231,7 +234,7 @@ class FromGeneric {
}
static Adapter buildAdapterFromBytecodes(MethodType internalType) {
throw new UnsupportedOperationException("NYI");
throw new UnsupportedOperationException("NYI "+internalType);
}
/**
......
......@@ -29,12 +29,12 @@ import sun.invoke.util.*;
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
/**
* Adapters which manage MethodHandle.invokeGeneric calls.
* Adapters which manage inexact MethodHandle.invoke calls.
* The JVM calls one of these when the exact type match fails.
* @author jrose
*/
class InvokeGeneric {
// erased type for the call, which originates from an invokeGeneric site
// erased type for the call, which originates from an inexact invoke site
private final MethodType erasedCallerType;
// an invoker of type (MT, MH; A...) -> R
private final MethodHandle initialInvoker;
......@@ -56,7 +56,7 @@ class InvokeGeneric {
}
/** Return the adapter information for this type's erasure. */
/*non-public*/ static MethodHandle genericInvokerOf(MethodType erasedCallerType) throws ReflectiveOperationException {
/*non-public*/ static MethodHandle generalInvokerOf(MethodType erasedCallerType) throws ReflectiveOperationException {
InvokeGeneric gen = new InvokeGeneric(erasedCallerType);
return gen.initialInvoker;
}
......
......@@ -43,10 +43,10 @@ class Invokers {
private /*lazy*/ MethodHandle erasedInvoker;
/*lazy*/ MethodHandle erasedInvokerWithDrops; // for InvokeGeneric
// generic (untyped) invoker for the outgoing call
private /*lazy*/ MethodHandle genericInvoker;
// general invoker for the outgoing call
private /*lazy*/ MethodHandle generalInvoker;
// generic (untyped) invoker for the outgoing call; accepts a single Object[]
// general invoker for the outgoing call; accepts a single Object[]
private final /*lazy*/ MethodHandle[] spreadInvokers;
// invoker for an unbound callsite
......@@ -77,13 +77,13 @@ class Invokers {
return invoker;
}
/*non-public*/ MethodHandle genericInvoker() {
/*non-public*/ MethodHandle generalInvoker() {
MethodHandle invoker1 = exactInvoker();
MethodHandle invoker = genericInvoker;
MethodHandle invoker = generalInvoker;
if (invoker != null) return invoker;
MethodType genericType = targetType.generic();
invoker = MethodHandles.convertArguments(invoker1, invokerType(genericType));
genericInvoker = invoker;
MethodType generalType = targetType.generic();
invoker = invoker1.asType(invokerType(generalType));
generalInvoker = invoker;
return invoker;
}
......@@ -93,9 +93,9 @@ class Invokers {
if (invoker != null) return invoker;
MethodType erasedType = targetType.erase();
if (erasedType == targetType.generic())
invoker = genericInvoker();
invoker = generalInvoker();
else
invoker = MethodHandles.convertArguments(invoker1, invokerType(erasedType));
invoker = invoker1.asType(invokerType(erasedType));
erasedInvoker = invoker;
return invoker;
}
......@@ -103,7 +103,7 @@ class Invokers {
/*non-public*/ MethodHandle spreadInvoker(int objectArgCount) {
MethodHandle vaInvoker = spreadInvokers[objectArgCount];
if (vaInvoker != null) return vaInvoker;
MethodHandle gInvoker = genericInvoker();
MethodHandle gInvoker = generalInvoker();
vaInvoker = gInvoker.asSpreader(Object[].class, targetType.parameterCount() - objectArgCount);
spreadInvokers[objectArgCount] = vaInvoker;
return vaInvoker;
......
......@@ -525,7 +525,7 @@ import static java.lang.invoke.MethodHandleStatics.*;
/** A factory type for resolving member names with the help of the VM.
* TBD: Define access-safe public constructors for this factory.
*/
public static class Factory {
/*non-public*/ static class Factory {
private Factory() { } // singleton pattern
static Factory INSTANCE = new Factory();
......
......@@ -115,6 +115,8 @@ class MethodHandleNatives {
/** Which conv-ops are implemented by the JVM? */
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();
static {
......@@ -141,6 +143,7 @@ class MethodHandleNatives {
if (CONV_OP_IMPLEMENTED_MASK_ == 0)
CONV_OP_IMPLEMENTED_MASK_ = DEFAULT_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.
......@@ -186,25 +189,26 @@ class MethodHandleNatives {
*/
static final int
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_PRIM_TO_PRIM = 0x3, // converts from one primitive to another
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_ROT_ARGS = 0x7, // rotate arguments (vminfo is displaced arg)
OP_DUP_ARGS = 0x8, // duplicates one or more arguments (at TOS)
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_FLYBY = 0xC, // operate first on reified argument list (NYI)
OP_RICOCHET = 0xD, // run an adapter chain on the return value (NYI)
OP_FOLD_ARGS = 0xC, // combine but do not remove arguments; prepend result
//OP_UNUSED_13 = 0xD, // unused code, perhaps for reified argument lists
CONV_OP_LIMIT = 0xE; // limit of CONV_OP enumeration
/** Shift and mask values for decoding the AMH.conversion field.
* These numbers are shared with the JVM for creating AMHs.
*/
static final int
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_SHIFT = 0, // position of bits in CONV_VMINFO_MASK
CONV_OP_SHIFT = 8, // position of bits in CONV_OP_MASK
......@@ -244,8 +248,9 @@ class MethodHandleNatives {
T_LONG = 11,
T_OBJECT = 12,
//T_ARRAY = 13
T_VOID = 14;
T_VOID = 14,
//T_ADDRESS = 15
T_ILLEGAL = 99;
/**
* Constant pool reference-kind codes, as used by CONSTANT_MethodHandle CP entries.
......@@ -273,16 +278,29 @@ class MethodHandleNatives {
try {
Field con = Constants.class.getDeclaredField(name);
int jval = con.getInt(null);
if (jval != vmval)
throw new InternalError(name+": JVM has "+vmval+" while Java has "+jval);
if (jval == vmval) continue;
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) {
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);
}
}
return true;
}
static {
verifyConstants();
assert(verifyConstants());
}
// Up-calls from the JVM.
......@@ -313,7 +331,7 @@ class MethodHandleNatives {
}
/**
* The JVM wants to use a MethodType with invokeGeneric. Give the runtime fair warning.
* The JVM wants to use a MethodType with inexact invoke. Give the runtime fair warning.
*/
static void notifyGenericMethodType(MethodType type) {
type.form().notifyGenericMethodType();
......@@ -323,15 +341,39 @@ class MethodHandleNatives {
* The JVM wants to raise an exception. Here's the path.
*/
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:
if (!(actual instanceof Class) && !(actual instanceof MethodType))
actual = actual.getClass();
if (actual != null)
message = "required "+required+" but encountered "+actual;
else
message = "required "+required;
if (message == null) {
if (!(actual instanceof Class) && !(actual instanceof MethodType))
actual = actual.getClass();
if (actual != null)
message = "required "+required+" but encountered "+actual;
else
message = "required "+required;
}
switch (code) {
case 190: // arraylength
throw new ArrayIndexOutOfBoundsException(message);
case 50: //_aaload
throw new ClassCastException(message);
case 192: // checkcast
throw new ClassCastException(message);
default:
......@@ -365,4 +407,13 @@ class MethodHandleNatives {
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;
}
static void checkSpreadArgument(Object av, int n) {
if (av == null ? n != 0 : ((Object[])av).length != n)
throw newIllegalArgumentException("Array is not of length "+n);
if (av == null) {
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)
......@@ -80,6 +89,9 @@ package java.lang.invoke;
/*non-public*/ static RuntimeException newIllegalArgumentException(String message, Object 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) {
Error err = new InternalError("uncaught exception");
err.initCause(ex);
......@@ -89,4 +101,8 @@ package java.lang.invoke;
if (obj != null) message = message + ": " + obj;
return message;
}
private static String message(String message, Object obj, Object obj2) {
if (obj != null || obj2 != null) message = message + ": " + obj + ", " + obj2;
return message;
}
}
......@@ -190,7 +190,7 @@ public class MethodHandles {
* is not symbolically accessible from the lookup class's loader,
* the lookup can still succeed.
* For example, lookups for {@code MethodHandle.invokeExact} and
* {@code MethodHandle.invokeGeneric} will always succeed, regardless of requested type.
* {@code MethodHandle.invoke} will always succeed, regardless of requested type.
* <li>If there is a security manager installed, it can forbid the lookup
* on various grounds (<a href="#secmgr">see below</a>).
* By contrast, the {@code ldc} instruction is not subject to
......@@ -590,10 +590,10 @@ public class MethodHandles {
* Because of the general equivalence between {@code invokevirtual}
* instructions and method handles produced by {@code findVirtual},
* if the class is {@code MethodHandle} and the name string is
* {@code invokeExact} or {@code invokeGeneric}, the resulting
* {@code invokeExact} or {@code invoke}, the resulting
* method handle is equivalent to one produced by
* {@link java.lang.invoke.MethodHandles#exactInvoker MethodHandles.exactInvoker} or
* {@link java.lang.invoke.MethodHandles#genericInvoker MethodHandles.genericInvoker}
* {@link java.lang.invoke.MethodHandles#invoker MethodHandles.invoker}
* with the same {@code type} argument.
*
* @param refc the class or interface from which the method is accessed
......@@ -1080,7 +1080,7 @@ return mh1;
MethodType rawType = mh.type();
if (rawType.parameterType(0) == caller) return mh;
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);
}
......@@ -1148,7 +1148,7 @@ return mh1;
* <li>an {@code Object[]} array containing more arguments
* </ul>
* <p>
* The invoker will behave like a call to {@link MethodHandle#invokeGeneric invokeGeneric} with
* The invoker will behave like a call to {@link MethodHandle#invoke invoke} with
* the indicated {@code type}.
* That is, if the target is exactly of the given {@code type}, it will behave
* like {@code invokeExact}; otherwise it behave as if {@link MethodHandle#asType asType}
......@@ -1166,7 +1166,7 @@ return mh1;
* <p>
* This method is equivalent to the following code (though it may be more efficient):
* <p><blockquote><pre>
MethodHandle invoker = MethodHandles.genericInvoker(type);
MethodHandle invoker = MethodHandles.invoker(type);
int spreadArgCount = type.parameterCount - objectArgCount;
invoker = invoker.asSpreader(Object[].class, spreadArgCount);
return invoker;
......@@ -1186,7 +1186,7 @@ return invoker;
/**
* Produces a special <em>invoker method handle</em> which can be used to
* invoke any method handle of the given type, as if by {@code invokeExact}.
* invoke any method handle of the given type, as if by {@link MethodHandle#invokeExact invokeExact}.
* The resulting invoker will have a type which is
* exactly equal to the desired type, except that it will accept
* an additional leading argument of type {@code MethodHandle}.
......@@ -1203,7 +1203,7 @@ publicLookup().findVirtual(MethodHandle.class, "invokeExact", type)
* For example, to emulate an {@code invokeExact} call to a variable method
* handle {@code M}, extract its type {@code T},
* look up the invoker method {@code X} for {@code T},
* and call the invoker method, as {@code X.invokeGeneric(T, A...)}.
* and call the invoker method, as {@code X.invoke(T, A...)}.
* (It would not work to call {@code X.invokeExact}, since the type {@code T}
* is unknown.)
* If spreading, collecting, or other argument transformations are required,
......@@ -1212,7 +1212,7 @@ publicLookup().findVirtual(MethodHandle.class, "invokeExact", type)
* <p>
* <em>(Note: The invoker method is not available via the Core Reflection API.
* An attempt to call {@linkplain java.lang.reflect.Method#invoke Method.invoke}
* on the declared {@code invokeExact} or {@code invokeGeneric} method will raise an
* on the declared {@code invokeExact} or {@code invoke} method will raise an
* {@link java.lang.UnsupportedOperationException UnsupportedOperationException}.)</em>
* <p>
* This method throws no reflective or security exceptions.
......@@ -1226,20 +1226,20 @@ publicLookup().findVirtual(MethodHandle.class, "invokeExact", type)
/**
* Produces a special <em>invoker method handle</em> which can be used to
* invoke any method handle of the given type, as if by {@code invokeGeneric}.
* invoke any method handle compatible with the given type, as if by {@link MethodHandle#invoke invoke}.
* The resulting invoker will have a type which is
* exactly equal to the desired type, except that it will accept
* an additional leading argument of type {@code MethodHandle}.
* <p>
* Before invoking its target, the invoker will apply reference casts as
* necessary and unbox and widen primitive arguments, as if by {@link #convertArguments convertArguments}.
* The return value of the invoker will be an {@code Object} reference,
* boxing a primitive value if the original type returns a primitive,
* and always null if the original type returns void.
* necessary and box, unbox, or widen primitive values, as if by {@link MethodHandle#asType asType}.
* Similarly, the return value will be converted as necessary.
* If the target is a {@linkplain MethodHandle#asVarargsCollector variable arity method handle},
* the required arity conversion will be made, again as if by {@link MethodHandle#asType asType}.
* <p>
* This method is equivalent to the following code (though it may be more efficient):
* <p><blockquote><pre>
publicLookup().findVirtual(MethodHandle.class, "invokeGeneric", type)
publicLookup().findVirtual(MethodHandle.class, "invoke", type)
* </pre></blockquote>
* <p>
* This method throws no reflective or security exceptions.
......@@ -1247,8 +1247,17 @@ publicLookup().findVirtual(MethodHandle.class, "invokeGeneric", type)
* @return a method handle suitable for invoking any method handle convertible to the given type
*/
static public
MethodHandle invoker(MethodType type) {
return type.invokers().generalInvoker();
}
/**
* <em>Temporary alias</em> for {@link #invoker}, for backward compatibility with some versions of JSR 292.
* @deprecated Will be removed for JSR 292 Proposed Final Draft.
*/
public static
MethodHandle genericInvoker(MethodType type) {
return type.invokers().genericInvoker();
return invoker(type);
}
/**
......@@ -1368,18 +1377,7 @@ publicLookup().findVirtual(MethodHandle.class, "invokeGeneric", type)
*/
public static
MethodHandle convertArguments(MethodHandle target, MethodType newType) {
MethodType oldType = target.type();
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;
return MethodHandleImpl.convertArguments(target, newType, 1);
}
/**
......@@ -1422,7 +1420,7 @@ publicLookup().findVirtual(MethodHandle.class, "invokeGeneric", type)
*/
public static
MethodHandle explicitCastArguments(MethodHandle target, MethodType newType) {
return convertArguments(target, newType); // FIXME!
return MethodHandleImpl.convertArguments(target, newType, 2);
}
/*
......@@ -1517,23 +1515,32 @@ assert((int)twice.invokeExact(21) == 42);
MethodHandle permuteArguments(MethodHandle target, MethodType newType, int... reorder) {
MethodType oldType = target.type();
checkReorder(reorder, newType, oldType);
return MethodHandleImpl.convertArguments(target,
return MethodHandleImpl.permuteArguments(target,
newType, oldType,
reorder);
}
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()) {
int limit = newType.parameterCount();
boolean bad = false;
for (int i : reorder) {
for (int j = 0; j < reorder.length; j++) {
int i = reorder[j];
if (i < 0 || i >= limit) {
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;
}
throw newIllegalArgumentException("bad reorder array");
throw newIllegalArgumentException("bad reorder array: "+Arrays.toString(reorder));
}
/**
......@@ -1622,7 +1629,7 @@ assert((int)twice.invokeExact(21) == 42);
if (type == void.class)
throw newIllegalArgumentException("void type");
Wrapper w = Wrapper.forPrimitiveType(type);
return identity(type).bindTo(w.convert(value, type));
return insertArguments(identity(type), 0, w.convert(value, type));
} else {
return identity(type).bindTo(type.cast(value));
}
......@@ -1857,7 +1864,8 @@ assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY
MethodHandle filterArguments(MethodHandle target, int pos, MethodHandle... filters) {
MethodType targetType = target.type();
MethodHandle adapter = target;
MethodType adapterType = targetType;
MethodType adapterType = null;
assert((adapterType = targetType) != null);
int maxPos = targetType.parameterCount();
if (pos + filters.length > maxPos)
throw newIllegalArgumentException("too many filters");
......@@ -1865,19 +1873,23 @@ assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY
for (MethodHandle filter : filters) {
curPos += 1;
if (filter == null) continue; // ignore null elements of filters
MethodType filterType = filter.type();
if (filterType.parameterCount() != 1
|| 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);
adapter = filterArgument(adapter, curPos, filter);
assert((adapterType = adapterType.changeParameterType(curPos, filter.type().parameterType(0))) != null);
}
MethodType midType = adapter.type();
if (midType != adapterType)
adapter = MethodHandleImpl.convertArguments(adapter, adapterType, midType, null);
assert(adapterType.equals(adapter.type()));
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
* its return value with a unary filter function.
......@@ -1913,14 +1925,26 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2
MethodHandle filterReturnValue(MethodHandle target, MethodHandle filter) {
MethodType targetType = target.type();
MethodType filterType = filter.type();
if (filterType.parameterCount() != 1
|| filterType.parameterType(0) != targetType.returnType())
throw newIllegalArgumentException("target and filter types do not match");
Class<?> rtype = targetType.returnType();
int filterValues = filterType.parameterCount();
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) },
// 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.
MethodHandle returner = dropArguments(filter, 1, targetType.parameterList());
return foldArguments(returner, target);
assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this class is deprecated
MethodHandle returner = dropArguments(filter, filterValues, targetType.parameterList());
result = foldArguments(returner, target);
assert(result.type().equals(newType));
return result;
}
/**
......@@ -1972,16 +1996,23 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2
MethodHandle foldArguments(MethodHandle target, MethodHandle combiner) {
MethodType targetType = target.type();
MethodType combinerType = combiner.type();
int foldPos = 0; // always at the head, at present
int foldArgs = combinerType.parameterCount();
boolean ok = (targetType.parameterCount() >= 1 + foldArgs);
if (ok && !combinerType.parameterList().equals(targetType.parameterList().subList(1, foldArgs+1)))
int foldVals = combinerType.returnType() == void.class ? 0 : 1;
int afterInsertPos = foldPos + foldVals;
boolean ok = (targetType.parameterCount() >= afterInsertPos + foldArgs);
if (ok && !(combinerType.parameterList()
.equals(targetType.parameterList().subList(afterInsertPos,
afterInsertPos + foldArgs))))
ok = false;
if (ok && !combinerType.returnType().equals(targetType.parameterType(0)))
if (ok && foldVals != 0 && !combinerType.returnType().equals(targetType.parameterType(0)))
ok = false;
if (!ok)
throw misMatchedTypes("target and combiner types", targetType, combinerType);
MethodType newType = targetType.dropParameterTypes(0, 1);
return MethodHandleImpl.foldArguments(target, newType, combiner);
MethodType newType = targetType.dropParameterTypes(foldPos, afterInsertPos);
MethodHandle res = MethodHandleImpl.foldArguments(target, newType, foldPos, combiner);
if (res == null) throw newIllegalArgumentException("cannot fold from "+newType+" to " +targetType);
return res;
}
/**
......@@ -2142,7 +2173,7 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2
* the given {@code target} on the incoming arguments,
* and returning or throwing whatever the {@code target}
* returns or throws. The invocation will be as if by
* {@code target.invokeGeneric}.
* {@code target.invoke}.
* The target's type will be checked before the
* instance is created, as if by a call to {@code asType},
* which may result in a {@code WrongMethodTypeException}.
......
......@@ -25,6 +25,7 @@
package java.lang.invoke;
import sun.invoke.util.Wrapper;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
......@@ -39,7 +40,7 @@ import static java.lang.invoke.MethodHandleStatics.*;
* matched between a method handle and all its callers,
* and the JVM's operations enforce this matching at, specifically
* during calls to {@link MethodHandle#invokeExact MethodHandle.invokeExact}
* and {@link MethodHandle#invokeGeneric MethodHandle.invokeGeneric}, and during execution
* and {@link MethodHandle#invoke MethodHandle.invoke}, and during execution
* of {@code invokedynamic} instructions.
* <p>
* The structure is a return type accompanied by any number of parameter types.
......@@ -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.
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
* All parameters and the return type will be {@code Object},
* except the final varargs parameter if any, which will be {@code Object[]}.
* @param objectArgCount number of parameters (excluding the varargs parameter if any)
* @param varargs whether there will be a varargs parameter, of type {@code Object[]}
* @return a totally generic method type, given only its count of parameters and varargs
* @throws IllegalArgumentException if {@code objectArgCount} is negative or greater than 255
* except the final array parameter if any, which will be {@code Object[]}.
* @param objectArgCount number of parameters (excluding the final array parameter if any)
* @param finalArray whether there will be a trailing array parameter, of type {@code Object[]}
* @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 (or 254, if {@code finalArray})
* @see #genericMethodType(int)
*/
public static
MethodType genericMethodType(int objectArgCount, boolean varargs) {
MethodType genericMethodType(int objectArgCount, boolean finalArray) {
MethodType mt;
checkSlotCount(objectArgCount);
int ivarargs = (!varargs ? 0 : 1);
int ivarargs = (!finalArray ? 0 : 1);
int ootIndex = objectArgCount*2 + ivarargs;
if (ootIndex < objectOnlyTypes.length) {
mt = objectOnlyTypes[ootIndex];
......@@ -294,7 +295,7 @@ class MethodType implements java.io.Serializable {
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
* All parameters and the return type will be Object.
* @param objectArgCount number of parameters
* @return a totally generic method type, given only its count of parameters
* @return a generally applicable method type, for all calls of the given argument count
* @throws IllegalArgumentException if {@code objectArgCount} is negative or greater than 255
* @see #genericMethodType(int, boolean)
*/
......@@ -626,6 +627,30 @@ class MethodType implements java.io.Serializable {
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
/** Reports the number of JVM stack slots required to invoke a method
......
......@@ -46,6 +46,7 @@ class MethodTypeForm {
final long argCounts; // packed slot & value counts
final long primCounts; // packed prim & double counts
final int vmslots; // total number of parameter slots
private Object vmlayout; // vm-specific information for calls
final MethodType erasedType; // the canonical erasure
/*lazy*/ MethodType primsAsBoxes; // replace prims by wrappers
......@@ -59,7 +60,7 @@ class MethodTypeForm {
/*lazy*/ FromGeneric fromGeneric; // convert cs. w/o prims to with
/*lazy*/ SpreadGeneric[] spreadGeneric; // expand one argument to many
/*lazy*/ FilterGeneric filterGeneric; // convert argument(s) on the fly
/*lazy*/ MethodHandle genericInvoker; // hook for invokeGeneric
/*lazy*/ MethodHandle genericInvoker; // hook for inexact invoke
public MethodType erasedType() {
return erasedType;
......@@ -460,9 +461,9 @@ class MethodTypeForm {
if (genericInvoker != null) return;
try {
// Trigger adapter creation.
genericInvoker = InvokeGeneric.genericInvokerOf(erasedType);
genericInvoker = InvokeGeneric.generalInvokerOf(erasedType);
} catch (Exception ex) {
Error err = new InternalError("Exception while resolving invokeGeneric");
Error err = new InternalError("Exception while resolving inexact invoke");
err.initCause(ex);
throw err;
}
......
......@@ -66,6 +66,10 @@ class SpreadGeneric {
this.entryPoint = ep[0];
}
static {
assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this class is deprecated
}
/** From targetType remove the last spreadCount arguments, and instead
* append a simple Object argument.
*/
......
......@@ -96,7 +96,7 @@ class ToGeneric {
ToGeneric va2 = ToGeneric.of(primsAtEnd);
this.adapter = va2.adapter;
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);
// example: for entryType of (int,Object,Object), the reordered
// type is (Object,Object,int) and the order is {1,2,0},
......@@ -128,7 +128,7 @@ class ToGeneric {
assert(eptWithInts.parameterType(i) == int.class);
MethodType nextType = midType.changeParameterType(i, int.class);
rawEntryPoint = MethodHandleImpl.convertArguments(
rawEntryPoint, nextType, midType, null);
rawEntryPoint, nextType, midType, 0);
midType = nextType;
}
}
......@@ -152,6 +152,10 @@ class ToGeneric {
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'.
* The values need to be reboxed for to match 'cooked'.
* Do this on the fly.
......@@ -171,7 +175,7 @@ class ToGeneric {
invoker.type().generic(), invoker, 0, MethodHandle.class);
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);
if (filteredInvoker == null) throw new InternalError();
}
......@@ -199,13 +203,13 @@ class ToGeneric {
assert(!rret.isPrimitive());
if (rret == Object.class && !mustCast)
return null;
return ValueConversions.cast(tret, false);
return ValueConversions.cast(tret);
} else if (tret == rret) {
return ValueConversions.unbox(tret, false);
return ValueConversions.unbox(tret);
} else {
assert(rret.isPrimitive());
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 {
}
static Adapter buildAdapterFromBytecodes(MethodType entryPointType) {
throw new UnsupportedOperationException("NYI");
throw new UnsupportedOperationException("NYI: "+entryPointType);
}
/**
......
......@@ -185,7 +185,7 @@
* The method handle constant produced for such a method behaves as if
* it were created by {@link java.lang.invoke.MethodHandle#asVarargsCollector asVarargsCollector}.
* In other words, the constant method handle will exhibit variable arity,
* when invoked via {@code invokeGeneric}.
* when invoked via {@code MethodHandle.invoke}.
* On the other hand, its behavior with respect to {@code invokeExact} will be the same
* as if the {@code varargs} bit were not set.
* <p>
......@@ -243,7 +243,7 @@
* <li>optionally, one or more <a href="#args">additional static arguments</a> </li>
* </ul>
* The method handle is then applied to the other values as if by
* {@link java.lang.invoke.MethodHandle#invokeGeneric invokeGeneric}.
* {@link java.lang.invoke.MethodHandle#invoke MethodHandle.invoke}.
* The returned result must be a {@link java.lang.invoke.CallSite CallSite} (or a subclass).
* The type of the call site's target must be exactly equal to the type
* derived from the dynamic call site's type descriptor and passed to
......@@ -251,7 +251,7 @@
* The call site then becomes permanently linked to the dynamic call site.
* <p>
* As long as each bootstrap method can be correctly invoked
* by <code>invokeGeneric</code>, its detailed type is arbitrary.
* by <code>MethodHandle.invoke</code>, its detailed type is arbitrary.
* For example, the first argument could be {@code Object}
* instead of {@code MethodHandles.Lookup}, and the return type
* could also be {@code Object} instead of {@code CallSite}.
......@@ -272,7 +272,7 @@
* (i.e., a {@code CONSTANT_Class}, {@code CONSTANT_MethodType},
* or {@code CONSTANT_MethodHandle} argument cannot be linked) </li>
* <li>the bootstrap method has the wrong arity,
* causing {@code invokeGeneric} to throw {@code WrongMethodTypeException} </li>
* causing {@code MethodHandle.invoke} to throw {@code WrongMethodTypeException} </li>
* <li>the bootstrap method has a wrong argument or return type </li>
* <li>the bootstrap method invocation completes abnormally </li>
* <li>the result from the bootstrap invocation is not a reference to
......@@ -381,10 +381,10 @@
* those values will be passed as additional arguments to the method handle.
* (Note that because there is a limit of 255 arguments to any method,
* at most 252 extra arguments can be supplied.)
* The bootstrap method will be invoked as if by either {@code invokeGeneric}
* The bootstrap method will be invoked as if by either {@code MethodHandle.invoke}
* or {@code invokeWithArguments}. (There is no way to tell the difference.)
* <p>
* The normal argument conversion rules for {@code invokeGeneric} apply to all stacked arguments.
* The normal argument conversion rules for {@code MethodHandle.invoke} apply to all stacked arguments.
* For example, if a pushed value is a primitive type, it may be converted to a reference by boxing conversion.
* If the bootstrap method is a variable arity method (its modifier bit {@code 0x0080} is set),
* then some or all of the arguments specified here may be collected into a trailing array parameter.
......@@ -419,8 +419,8 @@
* For example, the fourth argument could be {@code MethodHandle},
* if that is the type of the corresponding constant in
* the {@code CONSTANT_InvokeDynamic} entry.
* In that case, the {@code invokeGeneric} call will pass the extra method handle
* constant as an {@code Object}, but the type matching machinery of {@code invokeGeneric}
* In that case, the {@code MethodHandle.invoke} call will pass the extra method handle
* constant as an {@code Object}, but the type matching machinery of {@code MethodHandle.invoke}
* will cast the reference back to {@code MethodHandle} before invoking the bootstrap method.
* (If a string constant were passed instead, by badly generated code, that cast would then fail,
* resulting in a {@code BootstrapMethodError}.)
......
......@@ -54,9 +54,15 @@ public class VerifyType {
if (dst == void.class) return true; // drop any return value
if (isNullType(src)) return !dst.isPrimitive();
if (!src.isPrimitive()) return dst.isAssignableFrom(src);
if (!dst.isPrimitive()) return false;
// Verifier allows an int to carry byte, short, char, or even boolean:
if (dst == int.class) return Wrapper.forPrimitiveType(src).isSubwordOrInt();
return false;
Wrapper sw = Wrapper.forPrimitiveType(src);
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 {
return -1; // truncation may be required
if (!dw.isSigned() && sw.isSigned())
return -1; // sign elimination may be required
return 1;
}
if (src == float.class || dst == float.class) {
if (src == double.class || dst == double.class)
......
......@@ -26,37 +26,47 @@
package sun.invoke.util;
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
BYTE(Byte.class, byte.class, 'B', (Byte)(byte)0, Format.signed(8)),
SHORT(Short.class, short.class, 'S', (Short)(short)0, Format.signed(16)),
CHAR(Character.class, char.class, 'C', (Character)(char)0, Format.unsigned(16)),
INT(Integer.class, int.class, 'I', (Integer)(int)0, Format.signed(32)),
LONG(Long.class, long.class, 'J', (Long)(long)0, Format.signed(64)),
FLOAT(Float.class, float.class, 'F', (Float)(float)0, Format.floating(32)),
DOUBLE(Double.class, double.class, 'D', (Double)(double)0, Format.floating(64)),
//NULL(Null.class, null.class, 'N', null, Format.other(1)),
OBJECT(Object.class, Object.class, 'L', null, Format.other(1)),
BYTE(Byte.class, byte.class, 'B', (Byte)(byte)0, new byte[0], Format.signed(8)),
SHORT(Short.class, short.class, 'S', (Short)(short)0, new short[0], Format.signed(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, new int[0], Format.signed(32)),
LONG(Long.class, long.class, 'J', (Long)(long)0, new long[0], Format.signed(64)),
FLOAT(Float.class, float.class, 'F', (Float)(float)0, new float[0], Format.floating(32)),
DOUBLE(Double.class, double.class, 'D', (Double)(double)0, new double[0], Format.floating(64)),
//NULL(Null.class, null.class, 'N', null, 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(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<?> primitiveType;
private final char basicTypeChar;
private final Object zero;
private final Object emptyArray;
private final int format;
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.primitiveType = ptype;
this.basicTypeChar = tchar;
this.zero = zero;
this.emptyArray = emptyArray;
this.format = format;
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 {
static final int SLOT_SHIFT = 0, SIZE_SHIFT = 2, KIND_SHIFT = 12;
static final int
......@@ -114,16 +124,18 @@ public enum Wrapper {
public boolean isUnsigned() { return format >= Format.BOOLEAN && format < Format.FLOAT; }
/** Is the wrapped type either float or double? */
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?
* Cases:
* <ul>
* <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>conversion of {@code boolean} to any type
* </ul>
* These are the cases allowed by MethodHandle.asType and convertArguments.
*/
public boolean isConvertibleFrom(Wrapper source) {
if (this == source) return true;
......@@ -131,13 +143,75 @@ public enum Wrapper {
// At best, this is a narrowing conversion.
return false;
}
if ((this.format ^ source.format) == (Format.SHORT ^ Format.CHAR)) {
assert (this == SHORT && source == CHAR) || (this == CHAR && source == SHORT);
// All conversions are allowed in the enum order between floats and signed ints.
// 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;
}
// All signed and float conversions in the enum order are widening.
assert(this.isFloating() || this.isSigned());
assert(source.isFloating() || source.isSigned());
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.
* This will be a numeric zero for a number or character,
* false for a boolean, and null for a reference or void.
......@@ -549,7 +623,7 @@ public enum Wrapper {
}
private static boolean boolValue(long bits) {
//bits &= 1; // simple 31-bit zero extension
bits &= 1; // simple 31-bit zero extension
return (bits != 0);
}
......@@ -559,4 +633,31 @@ public enum Wrapper {
private static RuntimeException newIllegalArgumentException(String 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;
}
}
}
此差异已折叠。
......@@ -24,7 +24,7 @@
*/
/* @test
* @summary unit tests for java.lang.invoke.MethodHandle.invokeGeneric
* @summary unit tests for java.lang.invoke.MethodHandle.invoke
* @compile -target 7 InvokeGenericTest.java
* @run junit/othervm test.java.lang.invoke.InvokeGenericTest
*/
......@@ -53,6 +53,10 @@ public class InvokeGenericTest {
if (vstr != null) verbosity = Integer.parseInt(vstr);
}
public static void main(String... av) throws Throwable {
new InvokeGenericTest().testFirst();
}
@Test
public void testFirst() throws Throwable {
verbosity += 9; try {
......@@ -103,7 +107,7 @@ public class InvokeGenericTest {
void startTest(String name) {
if (testName != null) printCounts();
if (verbosity >= 1)
System.out.println(name);
System.out.println("["+name+"]");
posTests = negTests = 0;
testName = name;
}
......@@ -350,6 +354,30 @@ public class InvokeGenericTest {
String[] args = { "one", "two" };
MethodHandle mh = callable(Object.class, String.class);
Object res; List resl;
res = resl = (List) mh.invoke((String)args[0], (Object)args[1]);
//System.out.println(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
public void testAlternateName() throws Throwable {
startTest("testAlternateName");
countTest();
String[] args = { "one", "two" };
MethodHandle mh = callable(Object.class, String.class);
Object res; List resl;
res = resl = (List) mh.invokeGeneric((String)args[0], (Object)args[1]);
//System.out.println(res);
assertEquals(Arrays.asList(args), res);
......@@ -388,24 +416,24 @@ public class InvokeGenericTest {
try {
switch (args.length) {
case 0:
junk = target.invokeGeneric(); break;
junk = target.invoke(); break;
case 1:
junk = target.invokeGeneric(args[0]); break;
junk = target.invoke(args[0]); break;
case 2:
junk = target.invokeGeneric(args[0], args[1]); break;
junk = target.invoke(args[0], args[1]); break;
case 3:
junk = target.invokeGeneric(args[0], args[1], args[2]); break;
junk = target.invoke(args[0], args[1], args[2]); break;
case 4:
junk = target.invokeGeneric(args[0], args[1], args[2], args[3]); break;
junk = target.invoke(args[0], args[1], args[2], args[3]); break;
default:
junk = target.invokeWithArguments(args); break;
}
} catch (WrongMethodTypeException ex) {
return;
} 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.
......@@ -451,7 +479,7 @@ public class InvokeGenericTest {
startTest("testReferenceConversions");
toString_MH = LOOKUP.
findVirtual(Object.class, "toString", MethodType.methodType(String.class));
String[] args = { "one", "two" };
Object[] args = { "one", "two" };
for (MethodType type : allMethodTypes(2, Object.class, String.class, RandomInterface.class)) {
testReferenceConversions(type, args);
}
......@@ -463,7 +491,7 @@ public class InvokeGenericTest {
MethodHandle tsdrop = MethodHandles.dropArguments(toString_MH, 1, type.parameterList());
mh = MethodHandles.foldArguments(tsdrop, mh);
mh = mh.asType(type);
Object res = mh.invokeGeneric((String)args[0], (Object)args[1]);
Object res = mh.invoke((String)args[0], (Object)args[1]);
//System.out.println(res);
assertEquals(Arrays.asList(args).toString(), res);
}
......@@ -473,10 +501,10 @@ public class InvokeGenericTest {
public void testBoxConversions() throws Throwable {
startTest("testBoxConversions");
countTest();
Integer[] args = { 1, 2 };
Object[] args = { 1, 2 };
MethodHandle mh = callable(Object.class, int.class);
Object res; List resl;
res = resl = (List) mh.invokeGeneric((int)args[0], (Object)args[1]);
res = resl = (List) mh.invoke((int)args[0], (Object)args[1]);
//System.out.println(res);
assertEquals(Arrays.asList(args), res);
}
......
......@@ -170,8 +170,8 @@ assert(s.equals("savvy"));
mt = MethodType.methodType(java.util.List.class, Object[].class);
mh = lookup.findStatic(java.util.Arrays.class, "asList", mt);
assert(mh.isVarargsCollector());
x = mh.invokeGeneric("one", "two");
// invokeGeneric(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
x = mh.invoke("one", "two");
// invoke(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
assert(x.equals(java.util.Arrays.asList("one","two")));
// mt is (Object,Object,Object)Object
mt = MethodType.genericMethodType(3);
......@@ -199,12 +199,12 @@ mh.invokeExact(System.out, "Hello, world.");
MethodHandle asList = publicLookup()
.findStatic(Arrays.class, "asList", methodType(List.class, Object[].class))
.asVarargsCollector(Object[].class);
assertEquals("[]", asList.invokeGeneric().toString());
assertEquals("[1]", asList.invokeGeneric(1).toString());
assertEquals("[two, too]", asList.invokeGeneric("two", "too").toString());
assertEquals("[]", asList.invoke().toString());
assertEquals("[1]", asList.invoke(1).toString());
assertEquals("[two, too]", asList.invoke("two", "too").toString());
Object[] argv = { "three", "thee", "tee" };
assertEquals("[three, thee, tee]", asList.invokeGeneric(argv).toString());
List ls = (List) asList.invokeGeneric((Object)argv);
assertEquals("[three, thee, tee]", asList.invoke(argv).toString());
List ls = (List) asList.invoke((Object)argv);
assertEquals(1, ls.size());
assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0)));
}}
......@@ -218,9 +218,9 @@ MethodHandle vamh = publicLookup()
.asVarargsCollector(Object[].class);
MethodHandle mh = MethodHandles.exactInvoker(vamh.type()).bindTo(vamh);
assert(vamh.type().equals(mh.type()));
assertEquals("[1, 2, 3]", vamh.invokeGeneric(1,2,3).toString());
assertEquals("[1, 2, 3]", vamh.invoke(1,2,3).toString());
boolean failed = false;
try { mh.invokeGeneric(1,2,3); }
try { mh.invoke(1,2,3); }
catch (WrongMethodTypeException ex) { failed = true; }
assert(failed);
{}
......
......@@ -105,24 +105,6 @@ public class 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;
static int allPosTests, allNegTests;
int posTests, negTests;
......@@ -331,6 +313,9 @@ public class MethodHandlesTest {
static MethodHandle varargsArray(int 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. */
static MethodHandle varargsList(int arity, Class<?> rtype) {
MethodHandle list = varargsList(arity);
......@@ -865,7 +850,7 @@ public class MethodHandlesTest {
Class<?> type = (Class<?>) t[1];
Object value;
Field field;
try {
try {
field = HasFields.class.getDeclaredField(name);
} catch (Exception ex) {
throw new InternalError("no field HasFields."+name);
......@@ -1144,16 +1129,9 @@ public class MethodHandlesTest {
: MethodHandles.arrayElementSetter(arrayType);
assertSame(mh.type(), expType);
if (elemType != int.class && elemType != boolean.class) {
MethodType gtype;
if (true) { // FIXME: remove this path (and remove <void> below in the mh.invokes)
gtype = mh.type().changeParameterType(0, Object.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);
// FIXME: change Integer.class and (Integer) below to int.class and (int) below.
MethodType gtype = mh.type().generic().changeParameterType(1, Integer.class);
if (testSetter) gtype = gtype.changeReturnType(void.class);
mh = MethodHandles.convertArguments(mh, gtype);
}
Object sawValue, expValue;
......@@ -1169,7 +1147,7 @@ public class MethodHandlesTest {
else if (elemType == boolean.class)
mh.invokeExact((boolean[]) array, i, (boolean)random);
else
mh.invokeExact(array, i, random);
mh.invokeExact(array, (Integer)i, random);
assertEquals(model, array2list(array));
} else {
Array.set(array, i, random);
......@@ -1189,7 +1167,7 @@ public class MethodHandlesTest {
else if (elemType == boolean.class)
sawValue = (boolean) mh.invokeExact((boolean[]) array, i);
else
sawValue = mh.invokeExact(array, i);
sawValue = mh.invokeExact(array, (Integer)i);
assertEquals(sawValue, expValue);
assertEquals(model, array2list(array));
}
......@@ -1341,21 +1319,15 @@ public class MethodHandlesTest {
int numcases = 1;
for (int outargs = 0; outargs <= max; outargs++) {
if (outargs - inargs >= MAX_ARG_INCREASE) continue;
int[] reorder = new int[outargs];
int casStep = dilution + 1;
// Avoid some common factors:
while ((casStep > 2 && casStep % 2 == 0 && inargs % 2 == 0) ||
(casStep > 3 && casStep % 3 == 0 && inargs % 3 == 0))
casStep++;
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);
}
testPermuteArguments(args, types, outargs, numcases, casStep);
numcases *= inargs;
if (dilution > 10 && outargs >= 4) {
int[] reorder = new int[outargs];
// Do some special patterns, which we probably missed.
// Replication of a single argument or argument pair.
for (int i = 0; i < inargs; i++) {
......@@ -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) {
reorder = reorder.clone();
for (int i = 0, imax = reorder.length / 2; i < imax; i++) {
......@@ -1433,6 +1418,12 @@ public class MethodHandlesTest {
MethodHandle newTarget = MethodHandles.permuteArguments(target, inType, reorder);
Object result = newTarget.invokeWithArguments(args);
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);
}
......@@ -1456,26 +1447,27 @@ public class MethodHandlesTest {
}
public void testSpreadArguments(Class<?> argType, int pos, int nargs) throws Throwable {
countTest();
MethodHandle target = varargsArray(nargs);
MethodHandle target2 = changeArgTypes(target, argType);
Class<?> arrayType = java.lang.reflect.Array.newInstance(argType, 0).getClass();
MethodHandle target2 = varargsArray(arrayType, nargs);
MethodHandle target = target2.asType(target2.type().generic());
if (verbosity >= 3)
System.out.println("spread into "+target2+" ["+pos+".."+nargs+"]");
Object[] args = randomArgs(target2.type().parameterArray());
// make sure the target does what we think it does:
if (pos == 0 && nargs < 5) {
Object[] check = (Object[]) target.invokeWithArguments(args);
if (pos == 0 && nargs < 5 && !argType.isPrimitive()) {
Object[] check = (Object[]) (Object) target.invokeWithArguments(args);
assertArrayEquals(args, check);
switch (nargs) {
case 0:
check = (Object[]) target.invokeExact();
check = (Object[]) (Object) target.invokeExact();
assertArrayEquals(args, check);
break;
case 1:
check = (Object[]) target.invokeExact(args[0]);
check = (Object[]) (Object) target.invokeExact(args[0]);
assertArrayEquals(args, check);
break;
case 2:
check = (Object[]) target.invokeExact(args[0], args[1]);
check = (Object[]) (Object) target.invokeExact(args[0], args[1]);
assertArrayEquals(args, check);
break;
}
......@@ -1483,23 +1475,50 @@ public class MethodHandlesTest {
List<Class<?>> newParams = new ArrayList<Class<?>>(target2.type().parameterList());
{ // modify newParams in place
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);
MethodHandle result = target2.asSpreader(Object[].class, nargs-pos).asType(newType);
Object[] returnValue;
MethodType newType = MethodType.methodType(arrayType, newParams);
MethodHandle result = target2.asSpreader(arrayType, nargs-pos);
assert(result.type() == newType) : Arrays.asList(result, newType);
result = result.asType(newType.generic());
Object returnValue;
if (pos == 0) {
// In the following line, the first cast implies
// normal Object return value for the MH call (Object[])->Object,
// 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);
Object args2 = ValueConversions.changeArrayType(arrayType, Arrays.copyOfRange(args, pos, args.length));
returnValue = result.invokeExact(args2);
} else {
Object[] args1 = Arrays.copyOfRange(args, 0, pos+1);
args1[pos] = Arrays.copyOfRange(args, pos, args.length);
returnValue = (Object[]) result.invokeWithArguments(args1);
args1[pos] = ValueConversions.changeArrayType(arrayType, Arrays.copyOfRange(args, pos, args.length));
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
......@@ -1780,7 +1799,7 @@ public class MethodHandlesTest {
assertCalled("invokee", args);
// generic invoker
countTest();
inv = MethodHandles.genericInvoker(type);
inv = MethodHandles.invoker(type);
if (nargs <= 3) {
calledLog.clear();
switch (nargs) {
......@@ -2130,15 +2149,12 @@ public class MethodHandlesTest {
Object z = surprise.invokeExact(x);
System.out.println("Failed to throw; got z="+z);
assertTrue(false);
} catch (Exception ex) {
} catch (ClassCastException ex) {
if (verbosity > 2)
System.out.println("caught "+ex);
if (verbosity > 3)
ex.printStackTrace();
assertTrue(ex instanceof ClassCastException
// FIXME: accept only one of the two for any given unit test
|| ex instanceof WrongMethodTypeException
);
assertTrue(true); // all is well
}
}
......@@ -2328,6 +2344,34 @@ class ValueConversions {
// else need to spin bytecode or do something else fancy
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 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.
先完成此消息的编辑!
想要评论请 注册