提交 62c79ed9 编写于 作者: J jrose

6939861: JVM should handle more conversion operations

Summary: Integrate JDK code with JVM-supplied ricochet frames.
Reviewed-by: never, twisti
上级 73fdd5fc
......@@ -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:
......@@ -230,7 +234,7 @@ class FromGeneric {
}
static Adapter buildAdapterFromBytecodes(MethodType internalType) {
throw new UnsupportedOperationException("NYI");
throw new UnsupportedOperationException("NYI "+internalType);
}
/**
......
......@@ -82,7 +82,7 @@ class Invokers {
MethodHandle invoker = generalInvoker;
if (invoker != null) return invoker;
MethodType generalType = targetType.generic();
invoker = MethodHandles.convertArguments(invoker1, invokerType(generalType));
invoker = invoker1.asType(invokerType(generalType));
generalInvoker = invoker;
return invoker;
}
......@@ -95,7 +95,7 @@ class Invokers {
if (erasedType == targetType.generic())
invoker = generalInvoker();
else
invoker = MethodHandles.convertArguments(invoker1, invokerType(erasedType));
invoker = invoker1.asType(invokerType(erasedType));
erasedInvoker = invoker;
return invoker;
}
......
......@@ -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();
......
......@@ -26,6 +26,7 @@
package java.lang.invoke;
import sun.invoke.util.ValueConversions;
import static java.lang.invoke.MethodHandleStatics.*;
/**
......@@ -706,6 +707,9 @@ public abstract class MethodHandle {
* @see MethodHandles#convertArguments
*/
public MethodHandle asType(MethodType newType) {
if (!type.isConvertibleTo(newType)) {
throw new WrongMethodTypeException("cannot convert "+type+" to "+newType);
}
return MethodHandles.convertArguments(this, newType);
}
......@@ -748,13 +752,9 @@ public abstract class MethodHandle {
public MethodHandle asSpreader(Class<?> arrayType, int arrayLength) {
Class<?> arrayElement = arrayType.getComponentType();
if (arrayElement == null) throw newIllegalArgumentException("not an array type");
MethodType oldType = type();
int nargs = oldType.parameterCount();
int nargs = type().parameterCount();
if (nargs < arrayLength) throw newIllegalArgumentException("bad spread array length");
int keepPosArgs = nargs - arrayLength;
MethodType newType = oldType.dropParameterTypes(keepPosArgs, nargs);
newType = newType.insertParameterTypes(keepPosArgs, arrayType);
return MethodHandles.spreadArguments(this, newType);
return MethodHandleImpl.spreadArguments(this, arrayType, arrayLength);
}
/**
......@@ -797,15 +797,18 @@ public abstract class MethodHandle {
* @see #asVarargsCollector
*/
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();
if (arrayElement == null) throw newIllegalArgumentException("not an array type");
MethodType oldType = type();
int nargs = oldType.parameterCount();
if (nargs == 0) throw newIllegalArgumentException("no trailing argument");
MethodType newType = oldType.dropParameterTypes(nargs-1, nargs);
newType = newType.insertParameterTypes(nargs-1,
java.util.Collections.<Class<?>>nCopies(arrayLength, arrayElement));
return MethodHandles.collectArguments(this, newType);
if (arrayElement == null)
throw newIllegalArgumentException("not an array type", arrayType);
int nargs = type().parameterCount();
if (nargs == 0 || !type().parameterType(nargs-1).isAssignableFrom(arrayType))
throw newIllegalArgumentException("array type not assignable to trailing argument", this, arrayType);
}
/**
......
......@@ -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.
......@@ -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 (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,7 +63,16 @@ package java.lang.invoke;
}
static void checkSpreadArgument(Object av, int n) {
if (av == null ? n != 0 : ((Object[])av).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);
}
......@@ -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;
}
}
......@@ -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);
}
......@@ -1377,18 +1377,7 @@ publicLookup().findVirtual(MethodHandle.class, "invoke", 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);
}
/**
......@@ -1431,7 +1420,7 @@ publicLookup().findVirtual(MethodHandle.class, "invoke", type)
*/
public static
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);
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));
}
/**
......@@ -1631,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));
}
......@@ -1866,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");
......@@ -1874,17 +1873,21 @@ assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY
for (MethodHandle filter : filters) {
curPos += 1;
if (filter == null) continue; // ignore null elements of filters
adapter = filterArgument(adapter, curPos, filter);
assert((adapterType = adapterType.changeParameterType(curPos, filter.type().parameterType(0))) != 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(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();
if (midType != adapterType)
adapter = MethodHandleImpl.convertArguments(adapter, adapterType, midType, null);
return adapter;
|| filterType.returnType() != targetType.parameterType(pos))
throw newIllegalArgumentException("target and filter types do not match", targetType, filterType);
return MethodHandleImpl.filterArgument(target, pos, filter);
}
/**
......@@ -1922,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;
}
/**
......@@ -1981,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;
}
/**
......
......@@ -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;
......@@ -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];
......@@ -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
......
......@@ -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);
}
/**
......
......@@ -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;
}
}
}
此差异已折叠。
......@@ -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;
}
......@@ -355,6 +359,18 @@ public class InvokeGenericTest {
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");
......@@ -415,9 +431,9 @@ public class InvokeGenericTest {
} 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.
......
......@@ -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);
......@@ -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
......@@ -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.
先完成此消息的编辑!
想要评论请 注册