# HG changeset patch # User vlivanov # Date 1410362387 -14400 # Node ID 3bbb6a284bd43d440ddeef8a633cbf11d850fadd # Parent 5ff735dd0d52783189516d79a95cdf87c908f5b4 8049555: Move varargsArray from sun.invoke.util package to java.lang.invoke Reviewed-by: psandoz, iignatyev diff -r 5ff735dd0d52 -r 3bbb6a284bd4 jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java --- a/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java Wed Sep 10 19:19:47 2014 +0400 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java Wed Sep 10 19:19:47 2014 +0400 @@ -36,7 +36,6 @@ import static java.lang.invoke.MethodHandleStatics.*; import static java.lang.invoke.MethodHandleNatives.Constants.*; import java.lang.invoke.MethodHandleImpl.ArrayAccessor; -import sun.invoke.util.ValueConversions; import sun.invoke.util.VerifyAccess; import sun.invoke.util.VerifyType; import sun.invoke.util.Wrapper; @@ -904,7 +903,7 @@ if (!ptypes.equals(Collections.nCopies(size, arrayElementType))) return false; // Assume varargsArray caches pointers. - if (fn != ValueConversions.varargsArray(rtype, size)) + if (fn != MethodHandleImpl.varargsArray(rtype, size)) return false; return true; } diff -r 5ff735dd0d52 -r 3bbb6a284bd4 jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java --- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java Wed Sep 10 19:19:47 2014 +0400 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java Wed Sep 10 19:19:47 2014 +0400 @@ -28,7 +28,6 @@ import java.util.*; import java.lang.invoke.LambdaForm.BasicType; -import sun.invoke.util.*; import sun.misc.Unsafe; import static java.lang.invoke.MethodHandleStatics.*; @@ -988,7 +987,7 @@ MethodHandle target = this; if (arrayType != type().parameterType(collectArgPos)) target = convertArguments(type().changeParameterType(collectArgPos, arrayType)); - MethodHandle collector = ValueConversions.varargsArray(arrayType, arrayLength); + MethodHandle collector = MethodHandleImpl.varargsArray(arrayType, arrayLength); return MethodHandles.collectArguments(target, collectArgPos, collector); } diff -r 5ff735dd0d52 -r 3bbb6a284bd4 jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java --- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java Wed Sep 10 19:19:47 2014 +0400 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java Wed Sep 10 19:19:47 2014 +0400 @@ -27,8 +27,11 @@ import java.security.AccessController; import java.security.PrivilegedAction; +import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; +import java.util.Collections; +import java.util.List; + import sun.invoke.empty.Empty; import sun.invoke.util.ValueConversions; import sun.invoke.util.VerifyType; @@ -44,6 +47,20 @@ * @author jrose */ /*non-public*/ abstract class MethodHandleImpl { + // Do not adjust this except for special platforms: + private static final int MAX_ARITY; + static { + final Object[] values = { 255 }; + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Void run() { + values[0] = Integer.getInteger(MethodHandleImpl.class.getName()+".MAX_ARITY", 255); + return null; + } + }); + MAX_ARITY = (Integer) values[0]; + } + /// Factory methods to create method handles: static void initStatics() { @@ -516,6 +533,11 @@ static final NamedFunction NF_throwException; static final MethodHandle MH_castReference; + static final MethodHandle MH_copyAsPrimitiveArray; + static final MethodHandle MH_copyAsReferenceArray; + static final MethodHandle MH_fillNewTypedArray; + static final MethodHandle MH_fillNewArray; + static final MethodHandle MH_arrayIdentity; static { try { @@ -531,8 +553,18 @@ NF_selectAlternative.resolve(); NF_throwException.resolve(); - MethodType mt = MethodType.methodType(Object.class, Class.class, Object.class); - MH_castReference = IMPL_LOOKUP.findStatic(MHI, "castReference", mt); + MH_castReference = IMPL_LOOKUP.findStatic(MHI, "castReference", + MethodType.methodType(Object.class, Class.class, Object.class)); + MH_copyAsPrimitiveArray = IMPL_LOOKUP.findStatic(MHI, "copyAsPrimitiveArray", + MethodType.methodType(Object.class, Wrapper.class, Object[].class)); + MH_copyAsReferenceArray = IMPL_LOOKUP.findStatic(MHI, "copyAsReferenceArray", + MethodType.methodType(Object[].class, Class.class, Object[].class)); + MH_arrayIdentity = IMPL_LOOKUP.findStatic(MHI, "identity", + MethodType.methodType(Object[].class, Object[].class)); + MH_fillNewArray = IMPL_LOOKUP.findStatic(MHI, "fillNewArray", + MethodType.methodType(Object[].class, Integer.class, Object[].class)); + MH_fillNewTypedArray = IMPL_LOOKUP.findStatic(MHI, "fillNewTypedArray", + MethodType.methodType(Object[].class, Object[].class, Integer.class, Object[].class)); } catch (ReflectiveOperationException ex) { throw newInternalError(ex); } @@ -713,8 +745,7 @@ // Prepare auxiliary method handles used during LambdaForm interpretation. // Box arguments and wrap them into Object[]: ValueConversions.array(). MethodType varargsType = type.changeReturnType(Object[].class); - MethodHandle collectArgs = ValueConversions.varargsArray(type.parameterCount()) - .asType(varargsType); + MethodHandle collectArgs = varargsArray(type.parameterCount()).asType(varargsType); // Result unboxing: ValueConversions.unbox() OR ValueConversions.identity() OR ValueConversions.ignore(). MethodHandle unboxResult; if (type.returnType().isPrimitive()) { @@ -999,4 +1030,276 @@ return new WrappedMember(target, target.type(), member, null); } + /// Collection of multiple arguments. + + private static MethodHandle findCollector(String name, int nargs, Class rtype, Class... ptypes) { + MethodType type = MethodType.genericMethodType(nargs) + .changeReturnType(rtype) + .insertParameterTypes(0, ptypes); + try { + return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, name, type); + } catch (ReflectiveOperationException ex) { + return null; + } + } + + private static final Object[] NO_ARGS_ARRAY = {}; + private static Object[] makeArray(Object... args) { return args; } + private static Object[] array() { return NO_ARGS_ARRAY; } + private static Object[] array(Object a0) + { return makeArray(a0); } + private static Object[] array(Object a0, Object a1) + { return makeArray(a0, a1); } + private static Object[] array(Object a0, Object a1, Object a2) + { return makeArray(a0, a1, a2); } + private static Object[] array(Object a0, Object a1, Object a2, Object a3) + { return makeArray(a0, a1, a2, a3); } + private static Object[] array(Object a0, Object a1, Object a2, Object a3, + Object a4) + { return makeArray(a0, a1, a2, a3, a4); } + private static Object[] array(Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5) + { return makeArray(a0, a1, a2, a3, a4, a5); } + private static Object[] array(Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5, Object a6) + { return makeArray(a0, a1, a2, a3, a4, a5, a6); } + private static Object[] array(Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5, Object a6, Object a7) + { return makeArray(a0, a1, a2, a3, a4, a5, a6, a7); } + private static Object[] array(Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5, Object a6, Object a7, + Object a8) + { return makeArray(a0, a1, a2, a3, a4, a5, a6, a7, a8); } + private static Object[] array(Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5, Object a6, Object a7, + Object a8, Object a9) + { return makeArray(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); } + private static MethodHandle[] makeArrays() { + ArrayList mhs = new ArrayList<>(); + for (;;) { + MethodHandle mh = findCollector("array", mhs.size(), Object[].class); + if (mh == null) break; + mhs.add(mh); + } + assert(mhs.size() == 11); // current number of methods + return mhs.toArray(new MethodHandle[MAX_ARITY+1]); + } + private static final MethodHandle[] ARRAYS = makeArrays(); + + // filling versions of the above: + // using Integer len instead of int len and no varargs to avoid bootstrapping problems + private static Object[] fillNewArray(Integer len, Object[] /*not ...*/ args) { + Object[] a = new Object[len]; + fillWithArguments(a, 0, args); + return a; + } + private static Object[] fillNewTypedArray(Object[] example, Integer len, Object[] /*not ...*/ args) { + Object[] a = Arrays.copyOf(example, len); + fillWithArguments(a, 0, args); + return a; + } + private static void fillWithArguments(Object[] a, int pos, Object... args) { + System.arraycopy(args, 0, a, pos, args.length); + } + // using Integer pos instead of int pos to avoid bootstrapping problems + private static Object[] fillArray(Integer pos, Object[] a, Object a0) + { fillWithArguments(a, pos, a0); return a; } + private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1) + { fillWithArguments(a, pos, a0, a1); return a; } + private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2) + { fillWithArguments(a, pos, a0, a1, a2); return a; } + private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3) + { fillWithArguments(a, pos, a0, a1, a2, a3); return a; } + private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3, + Object a4) + { fillWithArguments(a, pos, a0, a1, a2, a3, a4); return a; } + private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5) + { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5); return a; } + private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5, Object a6) + { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6); return a; } + private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5, Object a6, Object a7) + { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7); return a; } + private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5, Object a6, Object a7, + Object a8) + { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7, a8); return a; } + private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5, Object a6, Object a7, + Object a8, Object a9) + { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); return a; } + private static MethodHandle[] makeFillArrays() { + ArrayList mhs = new ArrayList<>(); + mhs.add(null); // there is no empty fill; at least a0 is required + for (;;) { + MethodHandle mh = findCollector("fillArray", mhs.size(), Object[].class, Integer.class, Object[].class); + if (mh == null) break; + mhs.add(mh); + } + assert(mhs.size() == 11); // current number of methods + return mhs.toArray(new MethodHandle[0]); + } + private static final MethodHandle[] FILL_ARRAYS = makeFillArrays(); + + private static Object[] copyAsReferenceArray(Class arrayType, Object... a) { + return Arrays.copyOf(a, a.length, arrayType); + } + private static Object copyAsPrimitiveArray(Wrapper w, Object... boxes) { + Object a = w.makeArray(boxes.length); + w.copyArrayUnboxing(boxes, 0, a, 0, boxes.length); + return a; + } + + /** Return a method handle that takes the indicated number of Object + * arguments and returns an Object array of them, as if for varargs. + */ + static MethodHandle varargsArray(int nargs) { + MethodHandle mh = ARRAYS[nargs]; + if (mh != null) return mh; + mh = findCollector("array", nargs, Object[].class); + if (mh != null) return ARRAYS[nargs] = mh; + mh = buildVarargsArray(Lazy.MH_fillNewArray, Lazy.MH_arrayIdentity, nargs); + assert(assertCorrectArity(mh, nargs)); + return ARRAYS[nargs] = mh; + } + + private static boolean assertCorrectArity(MethodHandle mh, int arity) { + assert(mh.type().parameterCount() == arity) : "arity != "+arity+": "+mh; + return true; + } + + // Array identity function (used as Lazy.MH_arrayIdentity). + static T[] identity(T[] x) { + return x; + } + + private static MethodHandle buildVarargsArray(MethodHandle newArray, MethodHandle finisher, int nargs) { + // Build up the result mh as a sequence of fills like this: + // finisher(fill(fill(newArrayWA(23,x1..x10),10,x11..x20),20,x21..x23)) + // The various fill(_,10*I,___*[J]) are reusable. + int leftLen = Math.min(nargs, LEFT_ARGS); // absorb some arguments immediately + int rightLen = nargs - leftLen; + MethodHandle leftCollector = newArray.bindTo(nargs); + leftCollector = leftCollector.asCollector(Object[].class, leftLen); + MethodHandle mh = finisher; + if (rightLen > 0) { + MethodHandle rightFiller = fillToRight(LEFT_ARGS + rightLen); + if (mh == Lazy.MH_arrayIdentity) + mh = rightFiller; + else + mh = MethodHandles.collectArguments(mh, 0, rightFiller); + } + if (mh == Lazy.MH_arrayIdentity) + mh = leftCollector; + else + mh = MethodHandles.collectArguments(mh, 0, leftCollector); + return mh; + } + + private static final int LEFT_ARGS = (FILL_ARRAYS.length - 1); + private static final MethodHandle[] FILL_ARRAY_TO_RIGHT = new MethodHandle[MAX_ARITY+1]; + /** fill_array_to_right(N).invoke(a, argL..arg[N-1]) + * fills a[L]..a[N-1] with corresponding arguments, + * and then returns a. The value L is a global constant (LEFT_ARGS). + */ + private static MethodHandle fillToRight(int nargs) { + MethodHandle filler = FILL_ARRAY_TO_RIGHT[nargs]; + if (filler != null) return filler; + filler = buildFiller(nargs); + assert(assertCorrectArity(filler, nargs - LEFT_ARGS + 1)); + return FILL_ARRAY_TO_RIGHT[nargs] = filler; + } + private static MethodHandle buildFiller(int nargs) { + if (nargs <= LEFT_ARGS) + return Lazy.MH_arrayIdentity; // no args to fill; return the array unchanged + // we need room for both mh and a in mh.invoke(a, arg*[nargs]) + final int CHUNK = LEFT_ARGS; + int rightLen = nargs % CHUNK; + int midLen = nargs - rightLen; + if (rightLen == 0) { + midLen = nargs - (rightLen = CHUNK); + if (FILL_ARRAY_TO_RIGHT[midLen] == null) { + // build some precursors from left to right + for (int j = LEFT_ARGS % CHUNK; j < midLen; j += CHUNK) + if (j > LEFT_ARGS) fillToRight(j); + } + } + if (midLen < LEFT_ARGS) rightLen = nargs - (midLen = LEFT_ARGS); + assert(rightLen > 0); + MethodHandle midFill = fillToRight(midLen); // recursive fill + MethodHandle rightFill = FILL_ARRAYS[rightLen].bindTo(midLen); // [midLen..nargs-1] + assert(midFill.type().parameterCount() == 1 + midLen - LEFT_ARGS); + assert(rightFill.type().parameterCount() == 1 + rightLen); + + // Combine the two fills: + // right(mid(a, x10..x19), x20..x23) + // The final product will look like this: + // right(mid(newArrayLeft(24, x0..x9), x10..x19), x20..x23) + if (midLen == LEFT_ARGS) + return rightFill; + else + return MethodHandles.collectArguments(rightFill, 0, midFill); + } + + // Type-polymorphic version of varargs maker. + private static final ClassValue TYPED_COLLECTORS + = new ClassValue() { + @Override + protected MethodHandle[] computeValue(Class type) { + return new MethodHandle[256]; + } + }; + + static final int MAX_JVM_ARITY = 255; // limit imposed by the JVM + + /** Return a method handle that takes the indicated number of + * typed arguments and returns an array of them. + * The type argument is the array type. + */ + static MethodHandle varargsArray(Class arrayType, int nargs) { + Class elemType = arrayType.getComponentType(); + if (elemType == null) throw new IllegalArgumentException("not an array: "+arrayType); + // FIXME: Need more special casing and caching here. + if (nargs >= MAX_JVM_ARITY/2 - 1) { + int slots = nargs; + final int MAX_ARRAY_SLOTS = MAX_JVM_ARITY - 1; // 1 for receiver MH + if (arrayType == double[].class || arrayType == long[].class) + slots *= 2; + if (slots > MAX_ARRAY_SLOTS) + throw new IllegalArgumentException("too many arguments: "+arrayType.getSimpleName()+", length "+nargs); + } + if (elemType == Object.class) + return varargsArray(nargs); + // other cases: primitive arrays, subtypes of Object[] + MethodHandle cache[] = TYPED_COLLECTORS.get(elemType); + MethodHandle mh = nargs < cache.length ? cache[nargs] : null; + if (mh != null) return mh; + if (elemType.isPrimitive()) { + MethodHandle builder = Lazy.MH_fillNewArray; + MethodHandle producer = buildArrayProducer(arrayType); + mh = buildVarargsArray(builder, producer, nargs); + } else { + @SuppressWarnings("unchecked") + Class objArrayType = (Class) arrayType; + Object[] example = Arrays.copyOf(NO_ARGS_ARRAY, 0, objArrayType); + MethodHandle builder = Lazy.MH_fillNewTypedArray.bindTo(example); + MethodHandle producer = Lazy.MH_arrayIdentity; + mh = buildVarargsArray(builder, producer, nargs); + } + mh = mh.asType(MethodType.methodType(arrayType, Collections.>nCopies(nargs, elemType))); + assert(assertCorrectArity(mh, nargs)); + if (nargs < cache.length) + cache[nargs] = mh; + return mh; + } + + private static MethodHandle buildArrayProducer(Class arrayType) { + Class elemType = arrayType.getComponentType(); + if (elemType.isPrimitive()) + return Lazy.MH_copyAsPrimitiveArray.bindTo(Wrapper.forPrimitiveType(elemType)); + else + return Lazy.MH_copyAsReferenceArray.bindTo(arrayType); + } } diff -r 5ff735dd0d52 -r 3bbb6a284bd4 jdk/src/java.base/share/classes/sun/invoke/util/ValueConversions.java --- a/jdk/src/java.base/share/classes/sun/invoke/util/ValueConversions.java Wed Sep 10 19:19:47 2014 +0400 +++ b/jdk/src/java.base/share/classes/sun/invoke/util/ValueConversions.java Wed Sep 10 19:19:47 2014 +0400 @@ -29,30 +29,10 @@ import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles.Lookup; import java.lang.invoke.MethodType; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; import java.util.EnumMap; -import java.util.List; public class ValueConversions { private static final Class THIS_CLASS = ValueConversions.class; - // Do not adjust this except for special platforms: - private static final int MAX_ARITY; - static { - final Object[] values = { 255 }; - AccessController.doPrivileged(new PrivilegedAction() { - @Override - public Void run() { - values[0] = Integer.getInteger(THIS_CLASS.getName()+".MAX_ARITY", 255); - return null; - } - }); - MAX_ARITY = (Integer) values[0]; - } - private static final Lookup IMPL_LOOKUP = MethodHandles.lookup(); private static EnumMap[] newWrapperCaches(int n) { @@ -443,12 +423,7 @@ return x; } - private static ClassCastException newClassCastException(Class t, Object obj) { - return new ClassCastException("Cannot cast " + obj.getClass().getName() + " to " + t.getName()); - } - - private static final MethodHandle IDENTITY, CAST_REFERENCE, ZERO_OBJECT, IGNORE, EMPTY, - ARRAY_IDENTITY, FILL_NEW_TYPED_ARRAY, FILL_NEW_ARRAY; + private static final MethodHandle IDENTITY, CAST_REFERENCE, ZERO_OBJECT, IGNORE, EMPTY; static { try { MethodType idType = MethodType.genericMethodType(1); @@ -459,33 +434,11 @@ ZERO_OBJECT = IMPL_LOOKUP.findStatic(THIS_CLASS, "zeroObject", zeroObjectType); IGNORE = IMPL_LOOKUP.findStatic(THIS_CLASS, "ignore", ignoreType); EMPTY = IMPL_LOOKUP.findStatic(THIS_CLASS, "empty", ignoreType.dropParameterTypes(0, 1)); - ARRAY_IDENTITY = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", MethodType.methodType(Object[].class, Object[].class)); - FILL_NEW_ARRAY = IMPL_LOOKUP - .findStatic(THIS_CLASS, "fillNewArray", - MethodType.methodType(Object[].class, Integer.class, Object[].class)); - FILL_NEW_TYPED_ARRAY = IMPL_LOOKUP - .findStatic(THIS_CLASS, "fillNewTypedArray", - MethodType.methodType(Object[].class, Object[].class, Integer.class, Object[].class)); } catch (NoSuchMethodException | IllegalAccessException ex) { throw newInternalError("uncaught exception", ex); } } - // Varargs methods need to be in a separately initialized class, to avoid bootstrapping problems. - static class LazyStatics { - private static final MethodHandle COPY_AS_REFERENCE_ARRAY, COPY_AS_PRIMITIVE_ARRAY, MAKE_LIST; - static { - try { - //MAKE_ARRAY = IMPL_LOOKUP.findStatic(THIS_CLASS, "makeArray", MethodType.methodType(Object[].class, Object[].class)); - COPY_AS_REFERENCE_ARRAY = IMPL_LOOKUP.findStatic(THIS_CLASS, "copyAsReferenceArray", MethodType.methodType(Object[].class, Class.class, Object[].class)); - COPY_AS_PRIMITIVE_ARRAY = IMPL_LOOKUP.findStatic(THIS_CLASS, "copyAsPrimitiveArray", MethodType.methodType(Object.class, Wrapper.class, Object[].class)); - MAKE_LIST = IMPL_LOOKUP.findStatic(THIS_CLASS, "makeList", MethodType.methodType(List.class, Object[].class)); - } catch (ReflectiveOperationException ex) { - throw newInternalError("uncaught exception", ex); - } - } - } - private static final EnumMap[] WRAPPER_CASTS = newWrapperCaches(1); @@ -808,363 +761,6 @@ return Character.toUpperCase(x.charAt(0))+x.substring(1); } - /// Collection of multiple arguments. - - public static Object convertArrayElements(Class arrayType, Object array) { - Class src = array.getClass().getComponentType(); - Class dst = arrayType.getComponentType(); - if (src == null || dst == null) throw new IllegalArgumentException("not array type"); - Wrapper sw = (src.isPrimitive() ? Wrapper.forPrimitiveType(src) : null); - Wrapper dw = (dst.isPrimitive() ? Wrapper.forPrimitiveType(dst) : null); - int length; - if (sw == null) { - Object[] a = (Object[]) array; - length = a.length; - if (dw == null) - return Arrays.copyOf(a, length, arrayType.asSubclass(Object[].class)); - Object res = dw.makeArray(length); - dw.copyArrayUnboxing(a, 0, res, 0, length); - return res; - } - length = java.lang.reflect.Array.getLength(array); - Object[] res; - if (dw == null) { - res = Arrays.copyOf(NO_ARGS_ARRAY, length, arrayType.asSubclass(Object[].class)); - } else { - res = new Object[length]; - } - sw.copyArrayBoxing(array, 0, res, 0, length); - if (dw == null) return res; - Object a = dw.makeArray(length); - dw.copyArrayUnboxing(res, 0, a, 0, length); - return a; - } - - private static MethodHandle findCollector(String name, int nargs, Class rtype, Class... ptypes) { - MethodType type = MethodType.genericMethodType(nargs) - .changeReturnType(rtype) - .insertParameterTypes(0, ptypes); - try { - return IMPL_LOOKUP.findStatic(THIS_CLASS, name, type); - } catch (ReflectiveOperationException ex) { - return null; - } - } - - private static final Object[] NO_ARGS_ARRAY = {}; - private static Object[] makeArray(Object... args) { return args; } - private static Object[] array() { return NO_ARGS_ARRAY; } - private static Object[] array(Object a0) - { return makeArray(a0); } - private static Object[] array(Object a0, Object a1) - { return makeArray(a0, a1); } - private static Object[] array(Object a0, Object a1, Object a2) - { return makeArray(a0, a1, a2); } - private static Object[] array(Object a0, Object a1, Object a2, Object a3) - { return makeArray(a0, a1, a2, a3); } - private static Object[] array(Object a0, Object a1, Object a2, Object a3, - Object a4) - { return makeArray(a0, a1, a2, a3, a4); } - private static Object[] array(Object a0, Object a1, Object a2, Object a3, - Object a4, Object a5) - { return makeArray(a0, a1, a2, a3, a4, a5); } - private static Object[] array(Object a0, Object a1, Object a2, Object a3, - Object a4, Object a5, Object a6) - { return makeArray(a0, a1, a2, a3, a4, a5, a6); } - private static Object[] array(Object a0, Object a1, Object a2, Object a3, - Object a4, Object a5, Object a6, Object a7) - { return makeArray(a0, a1, a2, a3, a4, a5, a6, a7); } - private static Object[] array(Object a0, Object a1, Object a2, Object a3, - Object a4, Object a5, Object a6, Object a7, - Object a8) - { return makeArray(a0, a1, a2, a3, a4, a5, a6, a7, a8); } - private static Object[] array(Object a0, Object a1, Object a2, Object a3, - Object a4, Object a5, Object a6, Object a7, - Object a8, Object a9) - { return makeArray(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); } - private static MethodHandle[] makeArrays() { - ArrayList mhs = new ArrayList<>(); - for (;;) { - MethodHandle mh = findCollector("array", mhs.size(), Object[].class); - if (mh == null) break; - mhs.add(mh); - } - assert(mhs.size() == 11); // current number of methods - return mhs.toArray(new MethodHandle[MAX_ARITY+1]); - } - private static final MethodHandle[] ARRAYS = makeArrays(); - - // filling versions of the above: - // using Integer len instead of int len and no varargs to avoid bootstrapping problems - private static Object[] fillNewArray(Integer len, Object[] /*not ...*/ args) { - Object[] a = new Object[len]; - fillWithArguments(a, 0, args); - return a; - } - private static Object[] fillNewTypedArray(Object[] example, Integer len, Object[] /*not ...*/ args) { - Object[] a = Arrays.copyOf(example, len); - fillWithArguments(a, 0, args); - return a; - } - private static void fillWithArguments(Object[] a, int pos, Object... args) { - System.arraycopy(args, 0, a, pos, args.length); - } - // using Integer pos instead of int pos to avoid bootstrapping problems - private static Object[] fillArray(Integer pos, Object[] a, Object a0) - { fillWithArguments(a, pos, a0); return a; } - private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1) - { fillWithArguments(a, pos, a0, a1); return a; } - private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2) - { fillWithArguments(a, pos, a0, a1, a2); return a; } - private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3) - { fillWithArguments(a, pos, a0, a1, a2, a3); return a; } - private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3, - Object a4) - { fillWithArguments(a, pos, a0, a1, a2, a3, a4); return a; } - private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3, - Object a4, Object a5) - { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5); return a; } - private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3, - Object a4, Object a5, Object a6) - { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6); return a; } - private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3, - Object a4, Object a5, Object a6, Object a7) - { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7); return a; } - private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3, - Object a4, Object a5, Object a6, Object a7, - Object a8) - { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7, a8); return a; } - private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3, - Object a4, Object a5, Object a6, Object a7, - Object a8, Object a9) - { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); return a; } - private static MethodHandle[] makeFillArrays() { - ArrayList mhs = new ArrayList<>(); - mhs.add(null); // there is no empty fill; at least a0 is required - for (;;) { - MethodHandle mh = findCollector("fillArray", mhs.size(), Object[].class, Integer.class, Object[].class); - if (mh == null) break; - mhs.add(mh); - } - assert(mhs.size() == 11); // current number of methods - return mhs.toArray(new MethodHandle[0]); - } - private static final MethodHandle[] FILL_ARRAYS = makeFillArrays(); - - private static Object[] copyAsReferenceArray(Class arrayType, Object... a) { - return Arrays.copyOf(a, a.length, arrayType); - } - private static Object copyAsPrimitiveArray(Wrapper w, Object... boxes) { - Object a = w.makeArray(boxes.length); - w.copyArrayUnboxing(boxes, 0, a, 0, boxes.length); - return a; - } - - /** Return a method handle that takes the indicated number of Object - * arguments and returns an Object array of them, as if for varargs. - */ - public static MethodHandle varargsArray(int nargs) { - MethodHandle mh = ARRAYS[nargs]; - if (mh != null) return mh; - mh = findCollector("array", nargs, Object[].class); - if (mh != null) return ARRAYS[nargs] = mh; - mh = buildVarargsArray(FILL_NEW_ARRAY, ARRAY_IDENTITY, nargs); - assert(assertCorrectArity(mh, nargs)); - return ARRAYS[nargs] = mh; - } - - private static boolean assertCorrectArity(MethodHandle mh, int arity) { - assert(mh.type().parameterCount() == arity) : "arity != "+arity+": "+mh; - return true; - } - - private static MethodHandle buildVarargsArray(MethodHandle newArray, MethodHandle finisher, int nargs) { - // Build up the result mh as a sequence of fills like this: - // finisher(fill(fill(newArrayWA(23,x1..x10),10,x11..x20),20,x21..x23)) - // The various fill(_,10*I,___*[J]) are reusable. - int leftLen = Math.min(nargs, LEFT_ARGS); // absorb some arguments immediately - int rightLen = nargs - leftLen; - MethodHandle leftCollector = newArray.bindTo(nargs); - leftCollector = leftCollector.asCollector(Object[].class, leftLen); - MethodHandle mh = finisher; - if (rightLen > 0) { - MethodHandle rightFiller = fillToRight(LEFT_ARGS + rightLen); - if (mh == ARRAY_IDENTITY) - mh = rightFiller; - else - mh = MethodHandles.collectArguments(mh, 0, rightFiller); - } - if (mh == ARRAY_IDENTITY) - mh = leftCollector; - else - mh = MethodHandles.collectArguments(mh, 0, leftCollector); - return mh; - } - - private static final int LEFT_ARGS = (FILL_ARRAYS.length - 1); - private static final MethodHandle[] FILL_ARRAY_TO_RIGHT = new MethodHandle[MAX_ARITY+1]; - /** fill_array_to_right(N).invoke(a, argL..arg[N-1]) - * fills a[L]..a[N-1] with corresponding arguments, - * and then returns a. The value L is a global constant (LEFT_ARGS). - */ - private static MethodHandle fillToRight(int nargs) { - MethodHandle filler = FILL_ARRAY_TO_RIGHT[nargs]; - if (filler != null) return filler; - filler = buildFiller(nargs); - assert(assertCorrectArity(filler, nargs - LEFT_ARGS + 1)); - return FILL_ARRAY_TO_RIGHT[nargs] = filler; - } - private static MethodHandle buildFiller(int nargs) { - if (nargs <= LEFT_ARGS) - return ARRAY_IDENTITY; // no args to fill; return the array unchanged - // we need room for both mh and a in mh.invoke(a, arg*[nargs]) - final int CHUNK = LEFT_ARGS; - int rightLen = nargs % CHUNK; - int midLen = nargs - rightLen; - if (rightLen == 0) { - midLen = nargs - (rightLen = CHUNK); - if (FILL_ARRAY_TO_RIGHT[midLen] == null) { - // build some precursors from left to right - for (int j = LEFT_ARGS % CHUNK; j < midLen; j += CHUNK) - if (j > LEFT_ARGS) fillToRight(j); - } - } - if (midLen < LEFT_ARGS) rightLen = nargs - (midLen = LEFT_ARGS); - assert(rightLen > 0); - MethodHandle midFill = fillToRight(midLen); // recursive fill - MethodHandle rightFill = FILL_ARRAYS[rightLen].bindTo(midLen); // [midLen..nargs-1] - assert(midFill.type().parameterCount() == 1 + midLen - LEFT_ARGS); - assert(rightFill.type().parameterCount() == 1 + rightLen); - - // Combine the two fills: - // right(mid(a, x10..x19), x20..x23) - // The final product will look like this: - // right(mid(newArrayLeft(24, x0..x9), x10..x19), x20..x23) - if (midLen == LEFT_ARGS) - return rightFill; - else - return MethodHandles.collectArguments(rightFill, 0, midFill); - } - - // Type-polymorphic version of varargs maker. - private static final ClassValue TYPED_COLLECTORS - = new ClassValue() { - @Override - protected MethodHandle[] computeValue(Class type) { - return new MethodHandle[256]; - } - }; - - static final int MAX_JVM_ARITY = 255; // limit imposed by the JVM - - /** Return a method handle that takes the indicated number of - * typed arguments and returns an array of them. - * The type argument is the array type. - */ - public static MethodHandle varargsArray(Class arrayType, int nargs) { - Class elemType = arrayType.getComponentType(); - if (elemType == null) throw new IllegalArgumentException("not an array: "+arrayType); - // FIXME: Need more special casing and caching here. - if (nargs >= MAX_JVM_ARITY/2 - 1) { - int slots = nargs; - final int MAX_ARRAY_SLOTS = MAX_JVM_ARITY - 1; // 1 for receiver MH - if (arrayType == double[].class || arrayType == long[].class) - slots *= 2; - if (slots > MAX_ARRAY_SLOTS) - throw new IllegalArgumentException("too many arguments: "+arrayType.getSimpleName()+", length "+nargs); - } - if (elemType == Object.class) - return varargsArray(nargs); - // other cases: primitive arrays, subtypes of Object[] - MethodHandle cache[] = TYPED_COLLECTORS.get(elemType); - MethodHandle mh = nargs < cache.length ? cache[nargs] : null; - if (mh != null) return mh; - if (elemType.isPrimitive()) { - MethodHandle builder = FILL_NEW_ARRAY; - MethodHandle producer = buildArrayProducer(arrayType); - mh = buildVarargsArray(builder, producer, nargs); - } else { - @SuppressWarnings("unchecked") - Class objArrayType = (Class) arrayType; - Object[] example = Arrays.copyOf(NO_ARGS_ARRAY, 0, objArrayType); - MethodHandle builder = FILL_NEW_TYPED_ARRAY.bindTo(example); - MethodHandle producer = ARRAY_IDENTITY; - mh = buildVarargsArray(builder, producer, nargs); - } - mh = mh.asType(MethodType.methodType(arrayType, Collections.>nCopies(nargs, elemType))); - assert(assertCorrectArity(mh, nargs)); - if (nargs < cache.length) - cache[nargs] = mh; - return mh; - } - - private static MethodHandle buildArrayProducer(Class arrayType) { - Class elemType = arrayType.getComponentType(); - if (elemType.isPrimitive()) - return LazyStatics.COPY_AS_PRIMITIVE_ARRAY.bindTo(Wrapper.forPrimitiveType(elemType)); - else - return LazyStatics.COPY_AS_REFERENCE_ARRAY.bindTo(arrayType); - } - - // List version of varargs maker. - - private static final List NO_ARGS_LIST = Arrays.asList(NO_ARGS_ARRAY); - private static List makeList(Object... args) { return Arrays.asList(args); } - private static List list() { return NO_ARGS_LIST; } - private static List list(Object a0) - { return makeList(a0); } - private static List list(Object a0, Object a1) - { return makeList(a0, a1); } - private static List list(Object a0, Object a1, Object a2) - { return makeList(a0, a1, a2); } - private static List list(Object a0, Object a1, Object a2, Object a3) - { return makeList(a0, a1, a2, a3); } - private static List list(Object a0, Object a1, Object a2, Object a3, - Object a4) - { return makeList(a0, a1, a2, a3, a4); } - private static List list(Object a0, Object a1, Object a2, Object a3, - Object a4, Object a5) - { return makeList(a0, a1, a2, a3, a4, a5); } - private static List list(Object a0, Object a1, Object a2, Object a3, - Object a4, Object a5, Object a6) - { return makeList(a0, a1, a2, a3, a4, a5, a6); } - private static List list(Object a0, Object a1, Object a2, Object a3, - Object a4, Object a5, Object a6, Object a7) - { return makeList(a0, a1, a2, a3, a4, a5, a6, a7); } - private static List list(Object a0, Object a1, Object a2, Object a3, - Object a4, Object a5, Object a6, Object a7, - Object a8) - { return makeList(a0, a1, a2, a3, a4, a5, a6, a7, a8); } - private static List list(Object a0, Object a1, Object a2, Object a3, - Object a4, Object a5, Object a6, Object a7, - Object a8, Object a9) - { return makeList(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); } - private static MethodHandle[] makeLists() { - ArrayList mhs = new ArrayList<>(); - for (;;) { - MethodHandle mh = findCollector("list", mhs.size(), List.class); - if (mh == null) break; - mhs.add(mh); - } - assert(mhs.size() == 11); // current number of methods - return mhs.toArray(new MethodHandle[MAX_ARITY+1]); - } - private static final MethodHandle[] LISTS = makeLists(); - - /** Return a method handle that takes the indicated number of Object - * arguments and returns a List. - */ - public static MethodHandle varargsList(int nargs) { - MethodHandle mh = LISTS[nargs]; - if (mh != null) return mh; - mh = findCollector("list", nargs, List.class); - if (mh != null) return LISTS[nargs] = mh; - return LISTS[nargs] = buildVarargsList(nargs); - } - private static MethodHandle buildVarargsList(int nargs) { - return MethodHandles.filterReturnValue(varargsArray(nargs), LazyStatics.MAKE_LIST); - } - // handy shared exception makers (they simplify the common case code) private static InternalError newInternalError(String message, Throwable cause) { return new InternalError(message, cause); diff -r 5ff735dd0d52 -r 3bbb6a284bd4 jdk/test/java/lang/invoke/VarargsArrayTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/lang/invoke/VarargsArrayTest.java Wed Sep 10 19:19:47 2014 +0400 @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang.invoke; + +import sun.invoke.util.Wrapper; + +import java.util.Arrays; +import java.util.Collections; + +/* @test + * @summary unit tests for varargs array methods: MethodHandleInfo.varargsArray(int), + * MethodHandleInfo.varargsArray(Class,int) & MethodHandleInfo.varargsList(int) + * + * @run main/bootclasspath java.lang.invoke.VarargsArrayTest + * @run main/bootclasspath -DVarargsArrayTest.MAX_ARITY=255 -DVarargsArrayTest.START_ARITY=250 + * java.lang.invoke.VarargsArrayTest + */ + +/* This might take a while and burn lots of metadata: + * @run main/bootclasspath -DVarargsArrayTest.MAX_ARITY=255 -DVarargsArrayTest.EXHAUSTIVE=true java.lang.invoke.VarargsArrayTest + */ +public class VarargsArrayTest { + private static final Class CLASS = VarargsArrayTest.class; + private static final int MAX_ARITY = Integer.getInteger(CLASS.getSimpleName()+".MAX_ARITY", 40); + private static final int START_ARITY = Integer.getInteger(CLASS.getSimpleName()+".START_ARITY", 0); + private static final boolean EXHAUSTIVE = Boolean.getBoolean(CLASS.getSimpleName()+".EXHAUSTIVE"); + + public static void main(String[] args) throws Throwable { + testVarargsArray(); + testVarargsReferenceArray(); + testVarargsPrimitiveArray(); + } + + public static void testVarargsArray() throws Throwable { + final int MIN = START_ARITY; + final int MAX = MAX_ARITY-2; // 253+1 would cause parameter overflow with 'this' added + for (int nargs = MIN; nargs <= MAX; nargs = nextArgCount(nargs, 17, MAX)) { + MethodHandle target = MethodHandleImpl.varargsArray(nargs); + Object[] args = new Object[nargs]; + for (int i = 0; i < nargs; i++) + args[i] = "#"+i; + Object res = target.invokeWithArguments(args); + assertArrayEquals(args, (Object[])res); + } + } + + public static void testVarargsReferenceArray() throws Throwable { + testTypedVarargsArray(Object[].class); + testTypedVarargsArray(String[].class); + testTypedVarargsArray(Number[].class); + } + + public static void testVarargsPrimitiveArray() throws Throwable { + testTypedVarargsArray(int[].class); + testTypedVarargsArray(long[].class); + testTypedVarargsArray(byte[].class); + testTypedVarargsArray(boolean[].class); + testTypedVarargsArray(short[].class); + testTypedVarargsArray(char[].class); + testTypedVarargsArray(float[].class); + testTypedVarargsArray(double[].class); + } + + private static int nextArgCount(int nargs, int density, int MAX) { + if (EXHAUSTIVE) return nargs + 1; + if (nargs >= MAX) return Integer.MAX_VALUE; + int BOT = 20, TOP = MAX-5; + if (density < 10) { BOT = 10; MAX = TOP-2; } + if (nargs <= BOT || nargs >= TOP) { + ++nargs; + } else { + int bump = Math.max(1, 100 / density); + nargs += bump; + if (nargs > TOP) nargs = TOP; + } + return nargs; + } + + private static void testTypedVarargsArray(Class arrayType) throws Throwable { + Class elemType = arrayType.getComponentType(); + int MIN = START_ARITY; + int MAX = MAX_ARITY-2; // 253+1 would cause parameter overflow with 'this' added + int density = 3; + if (elemType == int.class || elemType == long.class) density = 7; + if (elemType == long.class || elemType == double.class) { MAX /= 2; MIN /= 2; } + for (int nargs = MIN; nargs <= MAX; nargs = nextArgCount(nargs, density, MAX)) { + Object[] args = makeTestArray(elemType, nargs); + MethodHandle varargsArray = MethodHandleImpl.varargsArray(arrayType, nargs); + MethodType vaType = varargsArray.type(); + assertEquals(arrayType, vaType.returnType()); + if (nargs != 0) { + assertEquals(elemType, vaType.parameterType(0)); + assertEquals(elemType, vaType.parameterType(vaType.parameterCount()-1)); + } + assertEquals(MethodType.methodType(arrayType, Collections.>nCopies(nargs, elemType)), + vaType); + Object res = varargsArray.invokeWithArguments(args); + assertEquals(res.getClass(), arrayType); + String resString = toArrayString(res); + assertEquals(Arrays.toString(args), resString); + + MethodHandle spreader = varargsArray.asSpreader(arrayType, nargs); + MethodType stype = spreader.type(); + assert(stype == MethodType.methodType(arrayType, arrayType)); + if (nargs <= 5) { + // invoke target as a spreader also: + @SuppressWarnings("cast") + Object res2 = spreader.invokeWithArguments((Object)res); + String res2String = toArrayString(res2); + assertEquals(Arrays.toString(args), res2String); + // invoke the spreader on a generic Object[] array; check for error + try { + Object res3 = spreader.invokeWithArguments((Object)args); + String res3String = toArrayString(res3); + assertTrue(arrayType.getName(), arrayType.isAssignableFrom(Object[].class)); + assertEquals(Arrays.toString(args), res3String); + } catch (ClassCastException ex) { + assertFalse(arrayType.getName(), arrayType.isAssignableFrom(Object[].class)); + } + } + if (nargs == 0) { + // invoke spreader on null arglist + Object res3 = spreader.invokeWithArguments((Object)null); + String res3String = toArrayString(res3); + assertEquals(Arrays.toString(args), res3String); + } + } + } + + private static Object[] makeTestArray(Class elemType, int len) { + Wrapper elem = null; + if (elemType.isPrimitive()) + elem = Wrapper.forPrimitiveType(elemType); + else if (Wrapper.isWrapperType(elemType)) + elem = Wrapper.forWrapperType(elemType); + Object[] args = new Object[len]; + for (int i = 0; i < len; i++) { + Object arg = i * 100; + if (elem == null) { + if (elemType == String.class) + arg = "#"+arg; + arg = elemType.cast(arg); // just to make sure + } else { + switch (elem) { + case BOOLEAN: arg = (i % 3 == 0); break; + case CHAR: arg = 'a' + i; break; + case LONG: arg = (long)i * 1000_000_000; break; + case FLOAT: arg = (float)i / 100; break; + case DOUBLE: arg = (double)i / 1000_000; break; + } + arg = elem.cast(arg, elemType); + } + args[i] = arg; + } + return args; + } + + private static String toArrayString(Object a) { + if (a == null) return "null"; + Class elemType = a.getClass().getComponentType(); + if (elemType == null) return a.toString(); + if (elemType.isPrimitive()) { + switch (Wrapper.forPrimitiveType(elemType)) { + case INT: return Arrays.toString((int[])a); + case BYTE: return Arrays.toString((byte[])a); + case BOOLEAN: return Arrays.toString((boolean[])a); + case SHORT: return Arrays.toString((short[])a); + case CHAR: return Arrays.toString((char[])a); + case FLOAT: return Arrays.toString((float[])a); + case LONG: return Arrays.toString((long[])a); + case DOUBLE: return Arrays.toString((double[])a); + } + } + return Arrays.toString((Object[])a); + } + + public static void assertArrayEquals(Object[] arr1, Object[] arr2) { + if (arr1 == null && arr2 == null) return; + if (arr1 != null && arr2 != null && arr1.length == arr2.length) { + for (int i = 0; i < arr1.length; i++) { + assertEquals(arr1[i], arr2[i]); + } + return; + } + throw new AssertionError(Arrays.deepToString(arr1) + " != " + Arrays.deepToString(arr2)); + } + + public static void assertEquals(Object o1, Object o2) { + if (o1 == null && o2 == null) return; + if (o1 != null && o1.equals(o2)) return; + throw new AssertionError(o1 + " != " + o2); + } + + public static void assertTrue(String msg, boolean b) { + if (!b) { + throw new AssertionError(msg); + } + } + + public static void assertFalse(String msg, boolean b) { + assertTrue(msg, !b); + } +} diff -r 5ff735dd0d52 -r 3bbb6a284bd4 jdk/test/sun/invoke/util/ValueConversionsTest.java --- a/jdk/test/sun/invoke/util/ValueConversionsTest.java Wed Sep 10 19:19:47 2014 +0400 +++ b/jdk/test/sun/invoke/util/ValueConversionsTest.java Wed Sep 10 19:19:47 2014 +0400 @@ -25,11 +25,11 @@ import sun.invoke.util.ValueConversions; import sun.invoke.util.Wrapper; + import java.lang.invoke.MethodType; import java.lang.invoke.MethodHandle; import java.io.Serializable; import java.util.Arrays; -import java.util.Collections; import org.junit.Test; import static org.junit.Assert.*; @@ -37,24 +37,13 @@ * @summary unit tests for value-type conversion utilities * @compile -XDignore.symbol.file ValueConversionsTest.java * @run junit/othervm test.sun.invoke.util.ValueConversionsTest - * @run junit/othervm - * -DValueConversionsTest.MAX_ARITY=255 -DValueConversionsTest.START_ARITY=250 - * test.sun.invoke.util.ValueConversionsTest */ -// This might take a while and burn lots of metadata: -// @run junit/othervm -DValueConversionsTest.MAX_ARITY=255 -DValueConversionsTest.EXHAUSTIVE=true test.sun.invoke.util.ValueConversionsTest - /** * * @author jrose */ public class ValueConversionsTest { - private static final Class CLASS = ValueConversionsTest.class; - private static final int MAX_ARITY = Integer.getInteger(CLASS.getSimpleName()+".MAX_ARITY", 40); - private static final int START_ARITY = Integer.getInteger(CLASS.getSimpleName()+".START_ARITY", 0); - private static final boolean EXHAUSTIVE = Boolean.getBoolean(CLASS.getSimpleName()+".EXHAUSTIVE"); - @Test public void testUnbox() throws Throwable { testUnbox(false); @@ -66,9 +55,7 @@ } private void testUnbox(boolean doCast) throws Throwable { - //System.out.println("unbox"); for (Wrapper dst : Wrapper.values()) { - //System.out.println(dst); for (Wrapper src : Wrapper.values()) { testUnbox(doCast, dst, src); } @@ -123,10 +110,8 @@ @Test public void testBox() throws Throwable { - //System.out.println("box"); for (Wrapper w : Wrapper.values()) { if (w == Wrapper.VOID) continue; // skip this; no unboxed form - //System.out.println(w); for (int n = -5; n < 10; n++) { Object box = w.wrap(n); MethodHandle boxer = ValueConversions.box(w.primitiveType()); @@ -151,7 +136,6 @@ @Test public void testCast() throws Throwable { - //System.out.println("cast"); Class[] types = { Object.class, Serializable.class, String.class, Number.class, Integer.class }; Object[] objects = { new Object(), Boolean.FALSE, "hello", (Long)12L, (Integer)6 }; for (Class dst : types) { @@ -160,7 +144,6 @@ for (Object obj : objects) { Class src = obj.getClass(); boolean canCast = dst.isAssignableFrom(src); - //System.out.println("obj="+obj+" <: dst="+dst+(canCast ? " (OK)" : " (will fail)")); try { Object result = caster.invokeExact(obj); if (canCast) @@ -177,24 +160,19 @@ @Test public void testIdentity() throws Throwable { - //System.out.println("identity"); MethodHandle id = ValueConversions.identity(); Object expResult = "foo"; Object result = id.invokeExact(expResult); - // compiler bug: ValueConversions.identity().invokeExact("bar"); assertEquals(expResult, result); } @Test public void testConvert() throws Throwable { - //System.out.println("convert"); for (long tval = 0, ctr = 0;;) { if (++ctr > 99999) throw new AssertionError("too many test values"); - // next test value: - //System.out.println(Long.toHexString(tval)); // prints 3776 test patterns + // prints 3776 test patterns (3776 = 8*59*8) tval = nextTestValue(tval); if (tval == 0) { - //System.out.println("test value count = "+ctr); // 3776 = 8*59*8 break; // repeat } } @@ -205,7 +183,6 @@ } } static void testConvert(Wrapper src, Wrapper dst, long tval) throws Throwable { - //System.out.println(src+" => "+dst); boolean testSingleCase = (tval != 0); final long tvalInit = tval; MethodHandle conv = ValueConversions.convertPrimitive(src, dst); @@ -269,169 +246,4 @@ } return tweakSign(ux); } - - @Test - public void testVarargsArray() throws Throwable { - //System.out.println("varargsArray"); - final int MIN = START_ARITY; - final int MAX = MAX_ARITY-2; // 253+1 would cause parameter overflow with 'this' added - for (int nargs = MIN; nargs <= MAX; nargs = nextArgCount(nargs, 17, MAX)) { - MethodHandle target = ValueConversions.varargsArray(nargs); - Object[] args = new Object[nargs]; - for (int i = 0; i < nargs; i++) - args[i] = "#"+i; - Object res = target.invokeWithArguments(args); - assertArrayEquals(args, (Object[])res); - } - } - - @Test - public void testVarargsReferenceArray() throws Throwable { - //System.out.println("varargsReferenceArray"); - testTypedVarargsArray(Object[].class); - testTypedVarargsArray(String[].class); - testTypedVarargsArray(Number[].class); - } - - @Test - public void testVarargsPrimitiveArray() throws Throwable { - //System.out.println("varargsPrimitiveArray"); - testTypedVarargsArray(int[].class); - testTypedVarargsArray(long[].class); - testTypedVarargsArray(byte[].class); - testTypedVarargsArray(boolean[].class); - testTypedVarargsArray(short[].class); - testTypedVarargsArray(char[].class); - testTypedVarargsArray(float[].class); - testTypedVarargsArray(double[].class); - } - - private static int nextArgCount(int nargs, int density, int MAX) { - if (EXHAUSTIVE) return nargs + 1; - if (nargs >= MAX) return Integer.MAX_VALUE; - int BOT = 20, TOP = MAX-5; - if (density < 10) { BOT = 10; MAX = TOP-2; } - if (nargs <= BOT || nargs >= TOP) { - ++nargs; - } else { - int bump = Math.max(1, 100 / density); - nargs += bump; - if (nargs > TOP) nargs = TOP; - } - return nargs; - } - - private void testTypedVarargsArray(Class arrayType) throws Throwable { - //System.out.println(arrayType.getSimpleName()); - Class elemType = arrayType.getComponentType(); - int MIN = START_ARITY; - int MAX = MAX_ARITY-2; // 253+1 would cause parameter overflow with 'this' added - int density = 3; - if (elemType == int.class || elemType == long.class) density = 7; - if (elemType == long.class || elemType == double.class) { MAX /= 2; MIN /= 2; } - for (int nargs = MIN; nargs <= MAX; nargs = nextArgCount(nargs, density, MAX)) { - Object[] args = makeTestArray(elemType, nargs); - MethodHandle varargsArray = ValueConversions.varargsArray(arrayType, nargs); - MethodType vaType = varargsArray.type(); - assertEquals(arrayType, vaType.returnType()); - if (nargs != 0) { - assertEquals(elemType, vaType.parameterType(0)); - assertEquals(elemType, vaType.parameterType(vaType.parameterCount()-1)); - } - assertEquals(MethodType.methodType(arrayType, Collections.>nCopies(nargs, elemType)), - vaType); - Object res = varargsArray.invokeWithArguments(args); - String resString = toArrayString(res); - assertEquals(Arrays.toString(args), resString); - - MethodHandle spreader = varargsArray.asSpreader(arrayType, nargs); - MethodType stype = spreader.type(); - assert(stype == MethodType.methodType(arrayType, arrayType)); - if (nargs <= 5) { - // invoke target as a spreader also: - @SuppressWarnings("cast") - Object res2 = spreader.invokeWithArguments((Object)res); - String res2String = toArrayString(res2); - assertEquals(Arrays.toString(args), res2String); - // invoke the spreader on a generic Object[] array; check for error - try { - Object res3 = spreader.invokeWithArguments((Object)args); - String res3String = toArrayString(res3); - assertTrue(arrayType.getName(), arrayType.isAssignableFrom(Object[].class)); - assertEquals(Arrays.toString(args), res3String); - } catch (ClassCastException ex) { - assertFalse(arrayType.getName(), arrayType.isAssignableFrom(Object[].class)); - } - } - if (nargs == 0) { - // invoke spreader on null arglist - Object res3 = spreader.invokeWithArguments((Object)null); - String res3String = toArrayString(res3); - assertEquals(Arrays.toString(args), res3String); - } - } - } - - private static Object[] makeTestArray(Class elemType, int len) { - Wrapper elem = null; - if (elemType.isPrimitive()) - elem = Wrapper.forPrimitiveType(elemType); - else if (Wrapper.isWrapperType(elemType)) - elem = Wrapper.forWrapperType(elemType); - Object[] args = new Object[len]; - for (int i = 0; i < len; i++) { - Object arg = i * 100; - if (elem == null) { - if (elemType == String.class) - arg = "#"+arg; - arg = elemType.cast(arg); // just to make sure - } else { - switch (elem) { - case BOOLEAN: arg = (i % 3 == 0); break; - case CHAR: arg = 'a' + i; break; - case LONG: arg = (long)i * 1000_000_000; break; - case FLOAT: arg = (float)i / 100; break; - case DOUBLE: arg = (double)i / 1000_000; break; - } - arg = elem.cast(arg, elemType); - } - args[i] = arg; - } - //System.out.println(elemType.getName()+Arrays.toString(args)); - return args; - } - - private static String toArrayString(Object a) { - if (a == null) return "null"; - Class elemType = a.getClass().getComponentType(); - if (elemType == null) return a.toString(); - if (elemType.isPrimitive()) { - switch (Wrapper.forPrimitiveType(elemType)) { - case INT: return Arrays.toString((int[])a); - case BYTE: return Arrays.toString((byte[])a); - case BOOLEAN: return Arrays.toString((boolean[])a); - case SHORT: return Arrays.toString((short[])a); - case CHAR: return Arrays.toString((char[])a); - case FLOAT: return Arrays.toString((float[])a); - case LONG: return Arrays.toString((long[])a); - case DOUBLE: return Arrays.toString((double[])a); - } - } - return Arrays.toString((Object[])a); - } - - @Test - public void testVarargsList() throws Throwable { - //System.out.println("varargsList"); - final int MIN = START_ARITY; - final int MAX = MAX_ARITY-2; // 253+1 would cause parameter overflow with 'this' added - for (int nargs = MIN; nargs <= MAX; nargs = nextArgCount(nargs, 7, MAX)) { - MethodHandle target = ValueConversions.varargsList(nargs); - Object[] args = new Object[nargs]; - for (int i = 0; i < nargs; i++) - args[i] = "#"+i; - Object res = target.invokeWithArguments(args); - assertEquals(Arrays.asList(args), res); - } - } }