diff -r eca8e9c3ec3e -r cd50c1894ce5 rt/emul/compact/src/main/java/sun/invoke/util/ValueConversions.java --- a/rt/emul/compact/src/main/java/sun/invoke/util/ValueConversions.java Sun Aug 17 20:09:05 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1188 +0,0 @@ -/* - * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package sun.invoke.util; - -import java.lang.invoke.MethodHandle; -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) { - @SuppressWarnings("unchecked") // generic array creation - EnumMap[] caches - = (EnumMap[]) new EnumMap[n]; - for (int i = 0; i < n; i++) - caches[i] = new EnumMap<>(Wrapper.class); - return caches; - } - - /// Converting references to values. - - // There are several levels of this unboxing conversions: - // no conversions: exactly Integer.valueOf, etc. - // implicit conversions sanctioned by JLS 5.1.2, etc. - // explicit conversions as allowed by explicitCastArguments - - static int unboxInteger(Object x, boolean cast) { - if (x instanceof Integer) - return ((Integer) x).intValue(); - return primitiveConversion(Wrapper.INT, x, cast).intValue(); - } - - static byte unboxByte(Object x, boolean cast) { - if (x instanceof Byte) - return ((Byte) x).byteValue(); - return primitiveConversion(Wrapper.BYTE, x, cast).byteValue(); - } - - static short unboxShort(Object x, boolean cast) { - if (x instanceof Short) - return ((Short) x).shortValue(); - return primitiveConversion(Wrapper.SHORT, x, cast).shortValue(); - } - - static boolean unboxBoolean(Object x, boolean cast) { - if (x instanceof Boolean) - return ((Boolean) x).booleanValue(); - return (primitiveConversion(Wrapper.BOOLEAN, x, cast).intValue() & 1) != 0; - } - - static char unboxCharacter(Object x, boolean cast) { - if (x instanceof Character) - return ((Character) x).charValue(); - return (char) primitiveConversion(Wrapper.CHAR, x, cast).intValue(); - } - - static long unboxLong(Object x, boolean cast) { - if (x instanceof Long) - return ((Long) x).longValue(); - return primitiveConversion(Wrapper.LONG, x, cast).longValue(); - } - - static float unboxFloat(Object x, boolean cast) { - if (x instanceof Float) - return ((Float) x).floatValue(); - return primitiveConversion(Wrapper.FLOAT, x, cast).floatValue(); - } - - static double unboxDouble(Object x, boolean cast) { - if (x instanceof Double) - return ((Double) x).doubleValue(); - return primitiveConversion(Wrapper.DOUBLE, x, cast).doubleValue(); - } - - private static MethodType unboxType(Wrapper wrap) { - return MethodType.methodType(wrap.primitiveType(), Object.class, boolean.class); - } - - private static final EnumMap[] - UNBOX_CONVERSIONS = newWrapperCaches(2); - - private static MethodHandle unbox(Wrapper wrap, boolean cast) { - EnumMap cache = UNBOX_CONVERSIONS[(cast?1:0)]; - MethodHandle mh = cache.get(wrap); - if (mh != null) { - return mh; - } - // slow path - switch (wrap) { - case OBJECT: - mh = IDENTITY; break; - case VOID: - mh = IGNORE; break; - } - if (mh != null) { - cache.put(wrap, mh); - return mh; - } - // look up the method - String name = "unbox" + wrap.wrapperSimpleName(); - MethodType type = unboxType(wrap); - try { - mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type); - } catch (ReflectiveOperationException ex) { - mh = null; - } - if (mh != null) { - mh = MethodHandles.insertArguments(mh, 1, cast); - cache.put(wrap, mh); - return mh; - } - throw new IllegalArgumentException("cannot find unbox adapter for " + wrap - + (cast ? " (cast)" : "")); - } - - public static MethodHandle unboxCast(Wrapper type) { - return unbox(type, true); - } - - public static MethodHandle unbox(Class type) { - return unbox(Wrapper.forPrimitiveType(type), false); - } - - public static MethodHandle unboxCast(Class type) { - return unbox(Wrapper.forPrimitiveType(type), true); - } - - static private final Integer ZERO_INT = 0, ONE_INT = 1; - - /// Primitive conversions - /** - * Produce a Number which represents the given value {@code x} - * according to the primitive type of the given wrapper {@code wrap}. - * Caller must invoke intValue, byteValue, longValue (etc.) on the result - * to retrieve the desired primitive value. - */ - public static Number primitiveConversion(Wrapper wrap, Object x, boolean cast) { - // Maybe merge this code with Wrapper.convert/cast. - Number res; - if (x == null) { - if (!cast) return null; - return ZERO_INT; - } - if (x instanceof Number) { - res = (Number) x; - } else if (x instanceof Boolean) { - res = ((boolean)x ? ONE_INT : ZERO_INT); - } else if (x instanceof Character) { - res = (int)(char)x; - } else { - // this will fail with the required ClassCastException: - res = (Number) x; - } - Wrapper xwrap = Wrapper.findWrapperType(x.getClass()); - if (xwrap == null || !cast && !wrap.isConvertibleFrom(xwrap)) - // this will fail with the required ClassCastException: - return (Number) wrap.wrapperType().cast(x); - return res; - } - - /** - * The JVM verifier allows boolean, byte, short, or char to widen to int. - * Support exactly this conversion, from a boxed value type Boolean, - * Byte, Short, Character, or Integer. - */ - public static int widenSubword(Object x) { - if (x instanceof Integer) - return (int) x; - else if (x instanceof Boolean) - return fromBoolean((boolean) x); - else if (x instanceof Character) - return (char) x; - else if (x instanceof Short) - return (short) x; - else if (x instanceof Byte) - return (byte) x; - else - // Fail with a ClassCastException. - return (int) x; - } - - /// Converting primitives to references - - static Integer boxInteger(int x) { - return x; - } - - static Byte boxByte(byte x) { - return x; - } - - static Short boxShort(short x) { - return x; - } - - static Boolean boxBoolean(boolean x) { - return x; - } - - static Character boxCharacter(char x) { - return x; - } - - static Long boxLong(long x) { - return x; - } - - static Float boxFloat(float x) { - return x; - } - - static Double boxDouble(double x) { - return x; - } - - private static MethodType boxType(Wrapper wrap) { - // be exact, since return casts are hard to compose - Class boxType = wrap.wrapperType(); - return MethodType.methodType(boxType, wrap.primitiveType()); - } - - private static final EnumMap[] - BOX_CONVERSIONS = newWrapperCaches(2); - - private static MethodHandle box(Wrapper wrap, boolean exact) { - EnumMap cache = BOX_CONVERSIONS[(exact?1:0)]; - MethodHandle mh = cache.get(wrap); - if (mh != null) { - return mh; - } - // slow path - switch (wrap) { - case OBJECT: - mh = IDENTITY; break; - case VOID: - mh = ZERO_OBJECT; - break; - } - if (mh != null) { - cache.put(wrap, mh); - return mh; - } - // look up the method - String name = "box" + wrap.wrapperSimpleName(); - MethodType type = boxType(wrap); - if (exact) { - try { - mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type); - } catch (ReflectiveOperationException ex) { - mh = null; - } - } else { - mh = box(wrap, !exact).asType(type.erase()); - } - if (mh != null) { - cache.put(wrap, mh); - return mh; - } - throw new IllegalArgumentException("cannot find box adapter for " - + wrap + (exact ? " (exact)" : "")); - } - - public static MethodHandle box(Class type) { - boolean exact = false; - // e.g., boxShort(short)Short if exact, - // e.g., boxShort(short)Object if !exact - return box(Wrapper.forPrimitiveType(type), exact); - } - - public static MethodHandle box(Wrapper type) { - boolean exact = false; - return box(type, exact); - } - - /// Constant functions - - static void ignore(Object x) { - // no value to return; this is an unbox of null - } - - static void empty() { - } - - static Object zeroObject() { - return null; - } - - static int zeroInteger() { - return 0; - } - - static long zeroLong() { - return 0; - } - - static float zeroFloat() { - return 0; - } - - static double zeroDouble() { - return 0; - } - - private static final EnumMap[] - CONSTANT_FUNCTIONS = newWrapperCaches(2); - - public static MethodHandle zeroConstantFunction(Wrapper wrap) { - EnumMap cache = CONSTANT_FUNCTIONS[0]; - MethodHandle mh = cache.get(wrap); - if (mh != null) { - return mh; - } - // slow path - MethodType type = MethodType.methodType(wrap.primitiveType()); - switch (wrap) { - case VOID: - mh = EMPTY; - break; - case OBJECT: - case INT: case LONG: case FLOAT: case DOUBLE: - try { - mh = IMPL_LOOKUP.findStatic(THIS_CLASS, "zero"+wrap.wrapperSimpleName(), type); - } catch (ReflectiveOperationException ex) { - mh = null; - } - break; - } - if (mh != null) { - cache.put(wrap, mh); - return mh; - } - - // use zeroInt and cast the result - if (wrap.isSubwordOrInt() && wrap != Wrapper.INT) { - mh = MethodHandles.explicitCastArguments(zeroConstantFunction(Wrapper.INT), type); - cache.put(wrap, mh); - return mh; - } - throw new IllegalArgumentException("cannot find zero constant for " + wrap); - } - - /// Converting references to references. - - /** - * Identity function. - * @param x an arbitrary reference value - * @return the same value x - */ - static T identity(T x) { - return x; - } - - static T[] identity(T[] x) { - return x; - } - - /** - * Identity function on ints. - * @param x an arbitrary int value - * @return the same value x - */ - static int identity(int x) { - return x; - } - - static byte identity(byte x) { - return x; - } - - static short identity(short x) { - return x; - } - - static boolean identity(boolean x) { - return x; - } - - static char identity(char x) { - return x; - } - - /** - * Identity function on longs. - * @param x an arbitrary long value - * @return the same value x - */ - static long identity(long x) { - return x; - } - - static float identity(float x) { - return x; - } - - static double identity(double x) { - return x; - } - - /** - * Identity function, with reference cast. - * @param t an arbitrary reference type - * @param x an arbitrary reference value - * @return the same value x - */ - @SuppressWarnings("unchecked") - static T castReference(Class t, U x) { - // inlined Class.cast because we can't ForceInline it - if (x != null && !t.isInstance(x)) - throw newClassCastException(t, x); - return (T) 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; - static { - try { - MethodType idType = MethodType.genericMethodType(1); - MethodType castType = idType.insertParameterTypes(0, Class.class); - MethodType ignoreType = idType.changeReturnType(void.class); - MethodType zeroObjectType = MethodType.genericMethodType(0); - IDENTITY = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", idType); - //CAST_REFERENCE = IMPL_LOOKUP.findVirtual(Class.class, "cast", idType); - CAST_REFERENCE = IMPL_LOOKUP.findStatic(THIS_CLASS, "castReference", castType); - 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); - - /** Return a method that casts its sole argument (an Object) to the given type - * and returns it as the given type. - */ - public static MethodHandle cast(Class type) { - if (type.isPrimitive()) throw new IllegalArgumentException("cannot cast primitive type "+type); - MethodHandle mh; - Wrapper wrap = null; - EnumMap cache = null; - if (Wrapper.isWrapperType(type)) { - wrap = Wrapper.forWrapperType(type); - cache = WRAPPER_CASTS[0]; - mh = cache.get(wrap); - if (mh != null) return mh; - } - mh = MethodHandles.insertArguments(CAST_REFERENCE, 0, type); - if (cache != null) - cache.put(wrap, mh); - return mh; - } - - public static MethodHandle identity() { - return IDENTITY; - } - - public static MethodHandle identity(Class type) { - if (!type.isPrimitive()) - // Reference identity has been moved into MethodHandles: - return MethodHandles.identity(type); - return identity(Wrapper.findPrimitiveType(type)); - } - - public static MethodHandle identity(Wrapper wrap) { - EnumMap cache = CONSTANT_FUNCTIONS[1]; - MethodHandle mh = cache.get(wrap); - if (mh != null) { - return mh; - } - // slow path - MethodType type = MethodType.methodType(wrap.primitiveType()); - if (wrap != Wrapper.VOID) - type = type.appendParameterTypes(wrap.primitiveType()); - try { - mh = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", type); - } catch (ReflectiveOperationException ex) { - mh = null; - } - if (mh == null && wrap == Wrapper.VOID) { - mh = EMPTY; // #(){} : #()void - } - if (mh != null) { - cache.put(wrap, mh); - return mh; - } - - if (mh != null) { - cache.put(wrap, mh); - return mh; - } - throw new IllegalArgumentException("cannot find identity for " + wrap); - } - - /// Primitive conversions. - // These are supported directly by the JVM, usually by a single instruction. - // In the case of narrowing to a subword, there may be a pair of instructions. - // In the case of booleans, there may be a helper routine to manage a 1-bit value. - // This is the full 8x8 matrix (minus the diagonal). - - // narrow double to all other types: - static float doubleToFloat(double x) { // bytecode: d2f - return (float) x; - } - static long doubleToLong(double x) { // bytecode: d2l - return (long) x; - } - static int doubleToInt(double x) { // bytecode: d2i - return (int) x; - } - static short doubleToShort(double x) { // bytecodes: d2i, i2s - return (short) x; - } - static char doubleToChar(double x) { // bytecodes: d2i, i2c - return (char) x; - } - static byte doubleToByte(double x) { // bytecodes: d2i, i2b - return (byte) x; - } - static boolean doubleToBoolean(double x) { - return toBoolean((byte) x); - } - - // widen float: - static double floatToDouble(float x) { // bytecode: f2d - return x; - } - // narrow float: - static long floatToLong(float x) { // bytecode: f2l - return (long) x; - } - static int floatToInt(float x) { // bytecode: f2i - return (int) x; - } - static short floatToShort(float x) { // bytecodes: f2i, i2s - return (short) x; - } - static char floatToChar(float x) { // bytecodes: f2i, i2c - return (char) x; - } - static byte floatToByte(float x) { // bytecodes: f2i, i2b - return (byte) x; - } - static boolean floatToBoolean(float x) { - return toBoolean((byte) x); - } - - // widen long: - static double longToDouble(long x) { // bytecode: l2d - return x; - } - static float longToFloat(long x) { // bytecode: l2f - return x; - } - // narrow long: - static int longToInt(long x) { // bytecode: l2i - return (int) x; - } - static short longToShort(long x) { // bytecodes: f2i, i2s - return (short) x; - } - static char longToChar(long x) { // bytecodes: f2i, i2c - return (char) x; - } - static byte longToByte(long x) { // bytecodes: f2i, i2b - return (byte) x; - } - static boolean longToBoolean(long x) { - return toBoolean((byte) x); - } - - // widen int: - static double intToDouble(int x) { // bytecode: i2d - return x; - } - static float intToFloat(int x) { // bytecode: i2f - return x; - } - static long intToLong(int x) { // bytecode: i2l - return x; - } - // narrow int: - static short intToShort(int x) { // bytecode: i2s - return (short) x; - } - static char intToChar(int x) { // bytecode: i2c - return (char) x; - } - static byte intToByte(int x) { // bytecode: i2b - return (byte) x; - } - static boolean intToBoolean(int x) { - return toBoolean((byte) x); - } - - // widen short: - static double shortToDouble(short x) { // bytecode: i2d (implicit 's2i') - return x; - } - static float shortToFloat(short x) { // bytecode: i2f (implicit 's2i') - return x; - } - static long shortToLong(short x) { // bytecode: i2l (implicit 's2i') - return x; - } - static int shortToInt(short x) { // (implicit 's2i') - return x; - } - // narrow short: - static char shortToChar(short x) { // bytecode: i2c (implicit 's2i') - return (char)x; - } - static byte shortToByte(short x) { // bytecode: i2b (implicit 's2i') - return (byte)x; - } - static boolean shortToBoolean(short x) { - return toBoolean((byte) x); - } - - // widen char: - static double charToDouble(char x) { // bytecode: i2d (implicit 'c2i') - return x; - } - static float charToFloat(char x) { // bytecode: i2f (implicit 'c2i') - return x; - } - static long charToLong(char x) { // bytecode: i2l (implicit 'c2i') - return x; - } - static int charToInt(char x) { // (implicit 'c2i') - return x; - } - // narrow char: - static short charToShort(char x) { // bytecode: i2s (implicit 'c2i') - return (short)x; - } - static byte charToByte(char x) { // bytecode: i2b (implicit 'c2i') - return (byte)x; - } - static boolean charToBoolean(char x) { - return toBoolean((byte) x); - } - - // widen byte: - static double byteToDouble(byte x) { // bytecode: i2d (implicit 'b2i') - return x; - } - static float byteToFloat(byte x) { // bytecode: i2f (implicit 'b2i') - return x; - } - static long byteToLong(byte x) { // bytecode: i2l (implicit 'b2i') - return x; - } - static int byteToInt(byte x) { // (implicit 'b2i') - return x; - } - static short byteToShort(byte x) { // bytecode: i2s (implicit 'b2i') - return (short)x; - } - static char byteToChar(byte x) { // bytecode: i2b (implicit 'b2i') - return (char)x; - } - // narrow byte to boolean: - static boolean byteToBoolean(byte x) { - return toBoolean(x); - } - - // widen boolean to all types: - static double booleanToDouble(boolean x) { - return fromBoolean(x); - } - static float booleanToFloat(boolean x) { - return fromBoolean(x); - } - static long booleanToLong(boolean x) { - return fromBoolean(x); - } - static int booleanToInt(boolean x) { - return fromBoolean(x); - } - static short booleanToShort(boolean x) { - return fromBoolean(x); - } - static char booleanToChar(boolean x) { - return (char)fromBoolean(x); - } - static byte booleanToByte(boolean x) { - return fromBoolean(x); - } - - // helpers to force boolean into the conversion scheme: - static boolean toBoolean(byte x) { - // see javadoc for MethodHandles.explicitCastArguments - return ((x & 1) != 0); - } - static byte fromBoolean(boolean x) { - // see javadoc for MethodHandles.explicitCastArguments - return (x ? (byte)1 : (byte)0); - } - - private static final EnumMap[] - CONVERT_PRIMITIVE_FUNCTIONS = newWrapperCaches(Wrapper.values().length); - - public static MethodHandle convertPrimitive(Wrapper wsrc, Wrapper wdst) { - EnumMap cache = CONVERT_PRIMITIVE_FUNCTIONS[wsrc.ordinal()]; - MethodHandle mh = cache.get(wdst); - if (mh != null) { - return mh; - } - // slow path - Class src = wsrc.primitiveType(); - Class dst = wdst.primitiveType(); - MethodType type = src == void.class ? MethodType.methodType(dst) : MethodType.methodType(dst, src); - if (wsrc == wdst) { - mh = identity(src); - } else if (wsrc == Wrapper.VOID) { - mh = zeroConstantFunction(wdst); - } else if (wdst == Wrapper.VOID) { - mh = MethodHandles.dropArguments(EMPTY, 0, src); // Defer back to MethodHandles. - } else if (wsrc == Wrapper.OBJECT) { - mh = unboxCast(dst); - } else if (wdst == Wrapper.OBJECT) { - mh = box(src); - } else { - assert(src.isPrimitive() && dst.isPrimitive()); - try { - mh = IMPL_LOOKUP.findStatic(THIS_CLASS, src.getSimpleName()+"To"+capitalize(dst.getSimpleName()), type); - } catch (ReflectiveOperationException ex) { - mh = null; - } - } - if (mh != null) { - assert(mh.type() == type) : mh; - cache.put(wdst, mh); - return mh; - } - - throw new IllegalArgumentException("cannot find primitive conversion function for " + - src.getSimpleName()+" -> "+dst.getSimpleName()); - } - - public static MethodHandle convertPrimitive(Class src, Class dst) { - return convertPrimitive(Wrapper.forPrimitiveType(src), Wrapper.forPrimitiveType(dst)); - } - - private static String capitalize(String x) { - 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); - } - private static InternalError newInternalError(Throwable cause) { - return new InternalError(cause); - } -}