jaroslav@1646: /* jaroslav@1646: * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. jaroslav@1646: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. jaroslav@1646: * jaroslav@1646: * This code is free software; you can redistribute it and/or modify it jaroslav@1646: * under the terms of the GNU General Public License version 2 only, as jaroslav@1646: * published by the Free Software Foundation. Oracle designates this jaroslav@1646: * particular file as subject to the "Classpath" exception as provided jaroslav@1646: * by Oracle in the LICENSE file that accompanied this code. jaroslav@1646: * jaroslav@1646: * This code is distributed in the hope that it will be useful, but WITHOUT jaroslav@1646: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or jaroslav@1646: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License jaroslav@1646: * version 2 for more details (a copy is included in the LICENSE file that jaroslav@1646: * accompanied this code). jaroslav@1646: * jaroslav@1646: * You should have received a copy of the GNU General Public License version jaroslav@1646: * 2 along with this work; if not, write to the Free Software Foundation, jaroslav@1646: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. jaroslav@1646: * jaroslav@1646: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA jaroslav@1646: * or visit www.oracle.com if you need additional information or have any jaroslav@1646: * questions. jaroslav@1646: */ jaroslav@1646: jaroslav@1646: package sun.invoke.util; jaroslav@1646: jaroslav@1646: import java.lang.invoke.MethodHandle; jaroslav@1646: import java.lang.invoke.MethodHandles; jaroslav@1646: import java.lang.invoke.MethodHandles.Lookup; jaroslav@1646: import java.lang.invoke.MethodType; jaroslav@1646: import java.security.AccessController; jaroslav@1646: import java.security.PrivilegedAction; jaroslav@1646: import java.util.ArrayList; jaroslav@1646: import java.util.Arrays; jaroslav@1646: import java.util.Collections; jaroslav@1646: import java.util.EnumMap; jaroslav@1646: import java.util.List; jaroslav@1646: jaroslav@1646: public class ValueConversions { jaroslav@1646: private static final Class THIS_CLASS = ValueConversions.class; jaroslav@1646: // Do not adjust this except for special platforms: jaroslav@1646: private static final int MAX_ARITY; jaroslav@1646: static { jaroslav@1646: final Object[] values = { 255 }; jaroslav@1646: AccessController.doPrivileged(new PrivilegedAction() { jaroslav@1646: @Override jaroslav@1646: public Void run() { jaroslav@1646: values[0] = Integer.getInteger(THIS_CLASS.getName()+".MAX_ARITY", 255); jaroslav@1646: return null; jaroslav@1646: } jaroslav@1646: }); jaroslav@1646: MAX_ARITY = (Integer) values[0]; jaroslav@1646: } jaroslav@1646: jaroslav@1646: private static final Lookup IMPL_LOOKUP = MethodHandles.lookup(); jaroslav@1646: jaroslav@1646: private static EnumMap[] newWrapperCaches(int n) { jaroslav@1646: @SuppressWarnings("unchecked") // generic array creation jaroslav@1646: EnumMap[] caches jaroslav@1646: = (EnumMap[]) new EnumMap[n]; jaroslav@1646: for (int i = 0; i < n; i++) jaroslav@1646: caches[i] = new EnumMap<>(Wrapper.class); jaroslav@1646: return caches; jaroslav@1646: } jaroslav@1646: jaroslav@1646: /// Converting references to values. jaroslav@1646: jaroslav@1646: // There are several levels of this unboxing conversions: jaroslav@1646: // no conversions: exactly Integer.valueOf, etc. jaroslav@1646: // implicit conversions sanctioned by JLS 5.1.2, etc. jaroslav@1646: // explicit conversions as allowed by explicitCastArguments jaroslav@1646: jaroslav@1646: static int unboxInteger(Object x, boolean cast) { jaroslav@1646: if (x instanceof Integer) jaroslav@1646: return ((Integer) x).intValue(); jaroslav@1646: return primitiveConversion(Wrapper.INT, x, cast).intValue(); jaroslav@1646: } jaroslav@1646: jaroslav@1646: static byte unboxByte(Object x, boolean cast) { jaroslav@1646: if (x instanceof Byte) jaroslav@1646: return ((Byte) x).byteValue(); jaroslav@1646: return primitiveConversion(Wrapper.BYTE, x, cast).byteValue(); jaroslav@1646: } jaroslav@1646: jaroslav@1646: static short unboxShort(Object x, boolean cast) { jaroslav@1646: if (x instanceof Short) jaroslav@1646: return ((Short) x).shortValue(); jaroslav@1646: return primitiveConversion(Wrapper.SHORT, x, cast).shortValue(); jaroslav@1646: } jaroslav@1646: jaroslav@1646: static boolean unboxBoolean(Object x, boolean cast) { jaroslav@1646: if (x instanceof Boolean) jaroslav@1646: return ((Boolean) x).booleanValue(); jaroslav@1646: return (primitiveConversion(Wrapper.BOOLEAN, x, cast).intValue() & 1) != 0; jaroslav@1646: } jaroslav@1646: jaroslav@1646: static char unboxCharacter(Object x, boolean cast) { jaroslav@1646: if (x instanceof Character) jaroslav@1646: return ((Character) x).charValue(); jaroslav@1646: return (char) primitiveConversion(Wrapper.CHAR, x, cast).intValue(); jaroslav@1646: } jaroslav@1646: jaroslav@1646: static long unboxLong(Object x, boolean cast) { jaroslav@1646: if (x instanceof Long) jaroslav@1646: return ((Long) x).longValue(); jaroslav@1646: return primitiveConversion(Wrapper.LONG, x, cast).longValue(); jaroslav@1646: } jaroslav@1646: jaroslav@1646: static float unboxFloat(Object x, boolean cast) { jaroslav@1646: if (x instanceof Float) jaroslav@1646: return ((Float) x).floatValue(); jaroslav@1646: return primitiveConversion(Wrapper.FLOAT, x, cast).floatValue(); jaroslav@1646: } jaroslav@1646: jaroslav@1646: static double unboxDouble(Object x, boolean cast) { jaroslav@1646: if (x instanceof Double) jaroslav@1646: return ((Double) x).doubleValue(); jaroslav@1646: return primitiveConversion(Wrapper.DOUBLE, x, cast).doubleValue(); jaroslav@1646: } jaroslav@1646: jaroslav@1646: private static MethodType unboxType(Wrapper wrap) { jaroslav@1646: return MethodType.methodType(wrap.primitiveType(), Object.class, boolean.class); jaroslav@1646: } jaroslav@1646: jaroslav@1646: private static final EnumMap[] jaroslav@1646: UNBOX_CONVERSIONS = newWrapperCaches(2); jaroslav@1646: jaroslav@1646: private static MethodHandle unbox(Wrapper wrap, boolean cast) { jaroslav@1646: EnumMap cache = UNBOX_CONVERSIONS[(cast?1:0)]; jaroslav@1646: MethodHandle mh = cache.get(wrap); jaroslav@1646: if (mh != null) { jaroslav@1646: return mh; jaroslav@1646: } jaroslav@1646: // slow path jaroslav@1646: switch (wrap) { jaroslav@1646: case OBJECT: jaroslav@1646: mh = IDENTITY; break; jaroslav@1646: case VOID: jaroslav@1646: mh = IGNORE; break; jaroslav@1646: } jaroslav@1646: if (mh != null) { jaroslav@1646: cache.put(wrap, mh); jaroslav@1646: return mh; jaroslav@1646: } jaroslav@1646: // look up the method jaroslav@1646: String name = "unbox" + wrap.wrapperSimpleName(); jaroslav@1646: MethodType type = unboxType(wrap); jaroslav@1646: try { jaroslav@1646: mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type); jaroslav@1646: } catch (ReflectiveOperationException ex) { jaroslav@1646: mh = null; jaroslav@1646: } jaroslav@1646: if (mh != null) { jaroslav@1646: mh = MethodHandles.insertArguments(mh, 1, cast); jaroslav@1646: cache.put(wrap, mh); jaroslav@1646: return mh; jaroslav@1646: } jaroslav@1646: throw new IllegalArgumentException("cannot find unbox adapter for " + wrap jaroslav@1646: + (cast ? " (cast)" : "")); jaroslav@1646: } jaroslav@1646: jaroslav@1646: public static MethodHandle unboxCast(Wrapper type) { jaroslav@1646: return unbox(type, true); jaroslav@1646: } jaroslav@1646: jaroslav@1646: public static MethodHandle unbox(Class type) { jaroslav@1646: return unbox(Wrapper.forPrimitiveType(type), false); jaroslav@1646: } jaroslav@1646: jaroslav@1646: public static MethodHandle unboxCast(Class type) { jaroslav@1646: return unbox(Wrapper.forPrimitiveType(type), true); jaroslav@1646: } jaroslav@1646: jaroslav@1646: static private final Integer ZERO_INT = 0, ONE_INT = 1; jaroslav@1646: jaroslav@1646: /// Primitive conversions jaroslav@1646: /** jaroslav@1646: * Produce a Number which represents the given value {@code x} jaroslav@1646: * according to the primitive type of the given wrapper {@code wrap}. jaroslav@1646: * Caller must invoke intValue, byteValue, longValue (etc.) on the result jaroslav@1646: * to retrieve the desired primitive value. jaroslav@1646: */ jaroslav@1646: public static Number primitiveConversion(Wrapper wrap, Object x, boolean cast) { jaroslav@1646: // Maybe merge this code with Wrapper.convert/cast. jaroslav@1646: Number res; jaroslav@1646: if (x == null) { jaroslav@1646: if (!cast) return null; jaroslav@1646: return ZERO_INT; jaroslav@1646: } jaroslav@1646: if (x instanceof Number) { jaroslav@1646: res = (Number) x; jaroslav@1646: } else if (x instanceof Boolean) { jaroslav@1646: res = ((boolean)x ? ONE_INT : ZERO_INT); jaroslav@1646: } else if (x instanceof Character) { jaroslav@1646: res = (int)(char)x; jaroslav@1646: } else { jaroslav@1646: // this will fail with the required ClassCastException: jaroslav@1646: res = (Number) x; jaroslav@1646: } jaroslav@1646: Wrapper xwrap = Wrapper.findWrapperType(x.getClass()); jaroslav@1646: if (xwrap == null || !cast && !wrap.isConvertibleFrom(xwrap)) jaroslav@1646: // this will fail with the required ClassCastException: jaroslav@1646: return (Number) wrap.wrapperType().cast(x); jaroslav@1646: return res; jaroslav@1646: } jaroslav@1646: jaroslav@1646: /** jaroslav@1646: * The JVM verifier allows boolean, byte, short, or char to widen to int. jaroslav@1646: * Support exactly this conversion, from a boxed value type Boolean, jaroslav@1646: * Byte, Short, Character, or Integer. jaroslav@1646: */ jaroslav@1646: public static int widenSubword(Object x) { jaroslav@1646: if (x instanceof Integer) jaroslav@1646: return (int) x; jaroslav@1646: else if (x instanceof Boolean) jaroslav@1646: return fromBoolean((boolean) x); jaroslav@1646: else if (x instanceof Character) jaroslav@1646: return (char) x; jaroslav@1646: else if (x instanceof Short) jaroslav@1646: return (short) x; jaroslav@1646: else if (x instanceof Byte) jaroslav@1646: return (byte) x; jaroslav@1646: else jaroslav@1646: // Fail with a ClassCastException. jaroslav@1646: return (int) x; jaroslav@1646: } jaroslav@1646: jaroslav@1646: /// Converting primitives to references jaroslav@1646: jaroslav@1646: static Integer boxInteger(int x) { jaroslav@1646: return x; jaroslav@1646: } jaroslav@1646: jaroslav@1646: static Byte boxByte(byte x) { jaroslav@1646: return x; jaroslav@1646: } jaroslav@1646: jaroslav@1646: static Short boxShort(short x) { jaroslav@1646: return x; jaroslav@1646: } jaroslav@1646: jaroslav@1646: static Boolean boxBoolean(boolean x) { jaroslav@1646: return x; jaroslav@1646: } jaroslav@1646: jaroslav@1646: static Character boxCharacter(char x) { jaroslav@1646: return x; jaroslav@1646: } jaroslav@1646: jaroslav@1646: static Long boxLong(long x) { jaroslav@1646: return x; jaroslav@1646: } jaroslav@1646: jaroslav@1646: static Float boxFloat(float x) { jaroslav@1646: return x; jaroslav@1646: } jaroslav@1646: jaroslav@1646: static Double boxDouble(double x) { jaroslav@1646: return x; jaroslav@1646: } jaroslav@1646: jaroslav@1646: private static MethodType boxType(Wrapper wrap) { jaroslav@1646: // be exact, since return casts are hard to compose jaroslav@1646: Class boxType = wrap.wrapperType(); jaroslav@1646: return MethodType.methodType(boxType, wrap.primitiveType()); jaroslav@1646: } jaroslav@1646: jaroslav@1646: private static final EnumMap[] jaroslav@1646: BOX_CONVERSIONS = newWrapperCaches(2); jaroslav@1646: jaroslav@1646: private static MethodHandle box(Wrapper wrap, boolean exact) { jaroslav@1646: EnumMap cache = BOX_CONVERSIONS[(exact?1:0)]; jaroslav@1646: MethodHandle mh = cache.get(wrap); jaroslav@1646: if (mh != null) { jaroslav@1646: return mh; jaroslav@1646: } jaroslav@1646: // slow path jaroslav@1646: switch (wrap) { jaroslav@1646: case OBJECT: jaroslav@1646: mh = IDENTITY; break; jaroslav@1646: case VOID: jaroslav@1646: mh = ZERO_OBJECT; jaroslav@1646: break; jaroslav@1646: } jaroslav@1646: if (mh != null) { jaroslav@1646: cache.put(wrap, mh); jaroslav@1646: return mh; jaroslav@1646: } jaroslav@1646: // look up the method jaroslav@1646: String name = "box" + wrap.wrapperSimpleName(); jaroslav@1646: MethodType type = boxType(wrap); jaroslav@1646: if (exact) { jaroslav@1646: try { jaroslav@1646: mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type); jaroslav@1646: } catch (ReflectiveOperationException ex) { jaroslav@1646: mh = null; jaroslav@1646: } jaroslav@1646: } else { jaroslav@1646: mh = box(wrap, !exact).asType(type.erase()); jaroslav@1646: } jaroslav@1646: if (mh != null) { jaroslav@1646: cache.put(wrap, mh); jaroslav@1646: return mh; jaroslav@1646: } jaroslav@1646: throw new IllegalArgumentException("cannot find box adapter for " jaroslav@1646: + wrap + (exact ? " (exact)" : "")); jaroslav@1646: } jaroslav@1646: jaroslav@1646: public static MethodHandle box(Class type) { jaroslav@1646: boolean exact = false; jaroslav@1646: // e.g., boxShort(short)Short if exact, jaroslav@1646: // e.g., boxShort(short)Object if !exact jaroslav@1646: return box(Wrapper.forPrimitiveType(type), exact); jaroslav@1646: } jaroslav@1646: jaroslav@1646: public static MethodHandle box(Wrapper type) { jaroslav@1646: boolean exact = false; jaroslav@1646: return box(type, exact); jaroslav@1646: } jaroslav@1646: jaroslav@1646: /// Constant functions jaroslav@1646: jaroslav@1646: static void ignore(Object x) { jaroslav@1646: // no value to return; this is an unbox of null jaroslav@1646: } jaroslav@1646: jaroslav@1646: static void empty() { jaroslav@1646: } jaroslav@1646: jaroslav@1646: static Object zeroObject() { jaroslav@1646: return null; jaroslav@1646: } jaroslav@1646: jaroslav@1646: static int zeroInteger() { jaroslav@1646: return 0; jaroslav@1646: } jaroslav@1646: jaroslav@1646: static long zeroLong() { jaroslav@1646: return 0; jaroslav@1646: } jaroslav@1646: jaroslav@1646: static float zeroFloat() { jaroslav@1646: return 0; jaroslav@1646: } jaroslav@1646: jaroslav@1646: static double zeroDouble() { jaroslav@1646: return 0; jaroslav@1646: } jaroslav@1646: jaroslav@1646: private static final EnumMap[] jaroslav@1646: CONSTANT_FUNCTIONS = newWrapperCaches(2); jaroslav@1646: jaroslav@1646: public static MethodHandle zeroConstantFunction(Wrapper wrap) { jaroslav@1646: EnumMap cache = CONSTANT_FUNCTIONS[0]; jaroslav@1646: MethodHandle mh = cache.get(wrap); jaroslav@1646: if (mh != null) { jaroslav@1646: return mh; jaroslav@1646: } jaroslav@1646: // slow path jaroslav@1646: MethodType type = MethodType.methodType(wrap.primitiveType()); jaroslav@1646: switch (wrap) { jaroslav@1646: case VOID: jaroslav@1646: mh = EMPTY; jaroslav@1646: break; jaroslav@1646: case OBJECT: jaroslav@1646: case INT: case LONG: case FLOAT: case DOUBLE: jaroslav@1646: try { jaroslav@1646: mh = IMPL_LOOKUP.findStatic(THIS_CLASS, "zero"+wrap.wrapperSimpleName(), type); jaroslav@1646: } catch (ReflectiveOperationException ex) { jaroslav@1646: mh = null; jaroslav@1646: } jaroslav@1646: break; jaroslav@1646: } jaroslav@1646: if (mh != null) { jaroslav@1646: cache.put(wrap, mh); jaroslav@1646: return mh; jaroslav@1646: } jaroslav@1646: jaroslav@1646: // use zeroInt and cast the result jaroslav@1646: if (wrap.isSubwordOrInt() && wrap != Wrapper.INT) { jaroslav@1646: mh = MethodHandles.explicitCastArguments(zeroConstantFunction(Wrapper.INT), type); jaroslav@1646: cache.put(wrap, mh); jaroslav@1646: return mh; jaroslav@1646: } jaroslav@1646: throw new IllegalArgumentException("cannot find zero constant for " + wrap); jaroslav@1646: } jaroslav@1646: jaroslav@1646: /// Converting references to references. jaroslav@1646: jaroslav@1646: /** jaroslav@1646: * Identity function. jaroslav@1646: * @param x an arbitrary reference value jaroslav@1646: * @return the same value x jaroslav@1646: */ jaroslav@1646: static T identity(T x) { jaroslav@1646: return x; jaroslav@1646: } jaroslav@1646: jaroslav@1646: static T[] identity(T[] x) { jaroslav@1646: return x; jaroslav@1646: } jaroslav@1646: jaroslav@1646: /** jaroslav@1646: * Identity function on ints. jaroslav@1646: * @param x an arbitrary int value jaroslav@1646: * @return the same value x jaroslav@1646: */ jaroslav@1646: static int identity(int x) { jaroslav@1646: return x; jaroslav@1646: } jaroslav@1646: jaroslav@1646: static byte identity(byte x) { jaroslav@1646: return x; jaroslav@1646: } jaroslav@1646: jaroslav@1646: static short identity(short x) { jaroslav@1646: return x; jaroslav@1646: } jaroslav@1646: jaroslav@1646: static boolean identity(boolean x) { jaroslav@1646: return x; jaroslav@1646: } jaroslav@1646: jaroslav@1646: static char identity(char x) { jaroslav@1646: return x; jaroslav@1646: } jaroslav@1646: jaroslav@1646: /** jaroslav@1646: * Identity function on longs. jaroslav@1646: * @param x an arbitrary long value jaroslav@1646: * @return the same value x jaroslav@1646: */ jaroslav@1646: static long identity(long x) { jaroslav@1646: return x; jaroslav@1646: } jaroslav@1646: jaroslav@1646: static float identity(float x) { jaroslav@1646: return x; jaroslav@1646: } jaroslav@1646: jaroslav@1646: static double identity(double x) { jaroslav@1646: return x; jaroslav@1646: } jaroslav@1646: jaroslav@1646: /** jaroslav@1646: * Identity function, with reference cast. jaroslav@1646: * @param t an arbitrary reference type jaroslav@1646: * @param x an arbitrary reference value jaroslav@1646: * @return the same value x jaroslav@1646: */ jaroslav@1646: @SuppressWarnings("unchecked") jaroslav@1646: static T castReference(Class t, U x) { jaroslav@1646: // inlined Class.cast because we can't ForceInline it jaroslav@1646: if (x != null && !t.isInstance(x)) jaroslav@1646: throw newClassCastException(t, x); jaroslav@1646: return (T) x; jaroslav@1646: } jaroslav@1646: jaroslav@1646: private static ClassCastException newClassCastException(Class t, Object obj) { jaroslav@1646: return new ClassCastException("Cannot cast " + obj.getClass().getName() + " to " + t.getName()); jaroslav@1646: } jaroslav@1646: jaroslav@1646: private static final MethodHandle IDENTITY, CAST_REFERENCE, ZERO_OBJECT, IGNORE, EMPTY, jaroslav@1646: ARRAY_IDENTITY, FILL_NEW_TYPED_ARRAY, FILL_NEW_ARRAY; jaroslav@1646: static { jaroslav@1646: try { jaroslav@1646: MethodType idType = MethodType.genericMethodType(1); jaroslav@1646: MethodType castType = idType.insertParameterTypes(0, Class.class); jaroslav@1646: MethodType ignoreType = idType.changeReturnType(void.class); jaroslav@1646: MethodType zeroObjectType = MethodType.genericMethodType(0); jaroslav@1646: IDENTITY = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", idType); jaroslav@1646: //CAST_REFERENCE = IMPL_LOOKUP.findVirtual(Class.class, "cast", idType); jaroslav@1646: CAST_REFERENCE = IMPL_LOOKUP.findStatic(THIS_CLASS, "castReference", castType); jaroslav@1646: ZERO_OBJECT = IMPL_LOOKUP.findStatic(THIS_CLASS, "zeroObject", zeroObjectType); jaroslav@1646: IGNORE = IMPL_LOOKUP.findStatic(THIS_CLASS, "ignore", ignoreType); jaroslav@1646: EMPTY = IMPL_LOOKUP.findStatic(THIS_CLASS, "empty", ignoreType.dropParameterTypes(0, 1)); jaroslav@1646: ARRAY_IDENTITY = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", MethodType.methodType(Object[].class, Object[].class)); jaroslav@1646: FILL_NEW_ARRAY = IMPL_LOOKUP jaroslav@1646: .findStatic(THIS_CLASS, "fillNewArray", jaroslav@1646: MethodType.methodType(Object[].class, Integer.class, Object[].class)); jaroslav@1646: FILL_NEW_TYPED_ARRAY = IMPL_LOOKUP jaroslav@1646: .findStatic(THIS_CLASS, "fillNewTypedArray", jaroslav@1646: MethodType.methodType(Object[].class, Object[].class, Integer.class, Object[].class)); jaroslav@1646: } catch (NoSuchMethodException | IllegalAccessException ex) { jaroslav@1646: throw newInternalError("uncaught exception", ex); jaroslav@1646: } jaroslav@1646: } jaroslav@1646: jaroslav@1646: // Varargs methods need to be in a separately initialized class, to avoid bootstrapping problems. jaroslav@1646: static class LazyStatics { jaroslav@1646: private static final MethodHandle COPY_AS_REFERENCE_ARRAY, COPY_AS_PRIMITIVE_ARRAY, MAKE_LIST; jaroslav@1646: static { jaroslav@1646: try { jaroslav@1646: //MAKE_ARRAY = IMPL_LOOKUP.findStatic(THIS_CLASS, "makeArray", MethodType.methodType(Object[].class, Object[].class)); jaroslav@1646: COPY_AS_REFERENCE_ARRAY = IMPL_LOOKUP.findStatic(THIS_CLASS, "copyAsReferenceArray", MethodType.methodType(Object[].class, Class.class, Object[].class)); jaroslav@1646: COPY_AS_PRIMITIVE_ARRAY = IMPL_LOOKUP.findStatic(THIS_CLASS, "copyAsPrimitiveArray", MethodType.methodType(Object.class, Wrapper.class, Object[].class)); jaroslav@1646: MAKE_LIST = IMPL_LOOKUP.findStatic(THIS_CLASS, "makeList", MethodType.methodType(List.class, Object[].class)); jaroslav@1646: } catch (ReflectiveOperationException ex) { jaroslav@1646: throw newInternalError("uncaught exception", ex); jaroslav@1646: } jaroslav@1646: } jaroslav@1646: } jaroslav@1646: jaroslav@1646: private static final EnumMap[] WRAPPER_CASTS jaroslav@1646: = newWrapperCaches(1); jaroslav@1646: jaroslav@1646: /** Return a method that casts its sole argument (an Object) to the given type jaroslav@1646: * and returns it as the given type. jaroslav@1646: */ jaroslav@1646: public static MethodHandle cast(Class type) { jaroslav@1646: if (type.isPrimitive()) throw new IllegalArgumentException("cannot cast primitive type "+type); jaroslav@1646: MethodHandle mh; jaroslav@1646: Wrapper wrap = null; jaroslav@1646: EnumMap cache = null; jaroslav@1646: if (Wrapper.isWrapperType(type)) { jaroslav@1646: wrap = Wrapper.forWrapperType(type); jaroslav@1646: cache = WRAPPER_CASTS[0]; jaroslav@1646: mh = cache.get(wrap); jaroslav@1646: if (mh != null) return mh; jaroslav@1646: } jaroslav@1646: mh = MethodHandles.insertArguments(CAST_REFERENCE, 0, type); jaroslav@1646: if (cache != null) jaroslav@1646: cache.put(wrap, mh); jaroslav@1646: return mh; jaroslav@1646: } jaroslav@1646: jaroslav@1646: public static MethodHandle identity() { jaroslav@1646: return IDENTITY; jaroslav@1646: } jaroslav@1646: jaroslav@1646: public static MethodHandle identity(Class type) { jaroslav@1646: if (!type.isPrimitive()) jaroslav@1646: // Reference identity has been moved into MethodHandles: jaroslav@1646: return MethodHandles.identity(type); jaroslav@1646: return identity(Wrapper.findPrimitiveType(type)); jaroslav@1646: } jaroslav@1646: jaroslav@1646: public static MethodHandle identity(Wrapper wrap) { jaroslav@1646: EnumMap cache = CONSTANT_FUNCTIONS[1]; jaroslav@1646: MethodHandle mh = cache.get(wrap); jaroslav@1646: if (mh != null) { jaroslav@1646: return mh; jaroslav@1646: } jaroslav@1646: // slow path jaroslav@1646: MethodType type = MethodType.methodType(wrap.primitiveType()); jaroslav@1646: if (wrap != Wrapper.VOID) jaroslav@1646: type = type.appendParameterTypes(wrap.primitiveType()); jaroslav@1646: try { jaroslav@1646: mh = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", type); jaroslav@1646: } catch (ReflectiveOperationException ex) { jaroslav@1646: mh = null; jaroslav@1646: } jaroslav@1646: if (mh == null && wrap == Wrapper.VOID) { jaroslav@1646: mh = EMPTY; // #(){} : #()void jaroslav@1646: } jaroslav@1646: if (mh != null) { jaroslav@1646: cache.put(wrap, mh); jaroslav@1646: return mh; jaroslav@1646: } jaroslav@1646: jaroslav@1646: if (mh != null) { jaroslav@1646: cache.put(wrap, mh); jaroslav@1646: return mh; jaroslav@1646: } jaroslav@1646: throw new IllegalArgumentException("cannot find identity for " + wrap); jaroslav@1646: } jaroslav@1646: jaroslav@1646: /// Primitive conversions. jaroslav@1646: // These are supported directly by the JVM, usually by a single instruction. jaroslav@1646: // In the case of narrowing to a subword, there may be a pair of instructions. jaroslav@1646: // In the case of booleans, there may be a helper routine to manage a 1-bit value. jaroslav@1646: // This is the full 8x8 matrix (minus the diagonal). jaroslav@1646: jaroslav@1646: // narrow double to all other types: jaroslav@1646: static float doubleToFloat(double x) { // bytecode: d2f jaroslav@1646: return (float) x; jaroslav@1646: } jaroslav@1646: static long doubleToLong(double x) { // bytecode: d2l jaroslav@1646: return (long) x; jaroslav@1646: } jaroslav@1646: static int doubleToInt(double x) { // bytecode: d2i jaroslav@1646: return (int) x; jaroslav@1646: } jaroslav@1646: static short doubleToShort(double x) { // bytecodes: d2i, i2s jaroslav@1646: return (short) x; jaroslav@1646: } jaroslav@1646: static char doubleToChar(double x) { // bytecodes: d2i, i2c jaroslav@1646: return (char) x; jaroslav@1646: } jaroslav@1646: static byte doubleToByte(double x) { // bytecodes: d2i, i2b jaroslav@1646: return (byte) x; jaroslav@1646: } jaroslav@1646: static boolean doubleToBoolean(double x) { jaroslav@1646: return toBoolean((byte) x); jaroslav@1646: } jaroslav@1646: jaroslav@1646: // widen float: jaroslav@1646: static double floatToDouble(float x) { // bytecode: f2d jaroslav@1646: return x; jaroslav@1646: } jaroslav@1646: // narrow float: jaroslav@1646: static long floatToLong(float x) { // bytecode: f2l jaroslav@1646: return (long) x; jaroslav@1646: } jaroslav@1646: static int floatToInt(float x) { // bytecode: f2i jaroslav@1646: return (int) x; jaroslav@1646: } jaroslav@1646: static short floatToShort(float x) { // bytecodes: f2i, i2s jaroslav@1646: return (short) x; jaroslav@1646: } jaroslav@1646: static char floatToChar(float x) { // bytecodes: f2i, i2c jaroslav@1646: return (char) x; jaroslav@1646: } jaroslav@1646: static byte floatToByte(float x) { // bytecodes: f2i, i2b jaroslav@1646: return (byte) x; jaroslav@1646: } jaroslav@1646: static boolean floatToBoolean(float x) { jaroslav@1646: return toBoolean((byte) x); jaroslav@1646: } jaroslav@1646: jaroslav@1646: // widen long: jaroslav@1646: static double longToDouble(long x) { // bytecode: l2d jaroslav@1646: return x; jaroslav@1646: } jaroslav@1646: static float longToFloat(long x) { // bytecode: l2f jaroslav@1646: return x; jaroslav@1646: } jaroslav@1646: // narrow long: jaroslav@1646: static int longToInt(long x) { // bytecode: l2i jaroslav@1646: return (int) x; jaroslav@1646: } jaroslav@1646: static short longToShort(long x) { // bytecodes: f2i, i2s jaroslav@1646: return (short) x; jaroslav@1646: } jaroslav@1646: static char longToChar(long x) { // bytecodes: f2i, i2c jaroslav@1646: return (char) x; jaroslav@1646: } jaroslav@1646: static byte longToByte(long x) { // bytecodes: f2i, i2b jaroslav@1646: return (byte) x; jaroslav@1646: } jaroslav@1646: static boolean longToBoolean(long x) { jaroslav@1646: return toBoolean((byte) x); jaroslav@1646: } jaroslav@1646: jaroslav@1646: // widen int: jaroslav@1646: static double intToDouble(int x) { // bytecode: i2d jaroslav@1646: return x; jaroslav@1646: } jaroslav@1646: static float intToFloat(int x) { // bytecode: i2f jaroslav@1646: return x; jaroslav@1646: } jaroslav@1646: static long intToLong(int x) { // bytecode: i2l jaroslav@1646: return x; jaroslav@1646: } jaroslav@1646: // narrow int: jaroslav@1646: static short intToShort(int x) { // bytecode: i2s jaroslav@1646: return (short) x; jaroslav@1646: } jaroslav@1646: static char intToChar(int x) { // bytecode: i2c jaroslav@1646: return (char) x; jaroslav@1646: } jaroslav@1646: static byte intToByte(int x) { // bytecode: i2b jaroslav@1646: return (byte) x; jaroslav@1646: } jaroslav@1646: static boolean intToBoolean(int x) { jaroslav@1646: return toBoolean((byte) x); jaroslav@1646: } jaroslav@1646: jaroslav@1646: // widen short: jaroslav@1646: static double shortToDouble(short x) { // bytecode: i2d (implicit 's2i') jaroslav@1646: return x; jaroslav@1646: } jaroslav@1646: static float shortToFloat(short x) { // bytecode: i2f (implicit 's2i') jaroslav@1646: return x; jaroslav@1646: } jaroslav@1646: static long shortToLong(short x) { // bytecode: i2l (implicit 's2i') jaroslav@1646: return x; jaroslav@1646: } jaroslav@1646: static int shortToInt(short x) { // (implicit 's2i') jaroslav@1646: return x; jaroslav@1646: } jaroslav@1646: // narrow short: jaroslav@1646: static char shortToChar(short x) { // bytecode: i2c (implicit 's2i') jaroslav@1646: return (char)x; jaroslav@1646: } jaroslav@1646: static byte shortToByte(short x) { // bytecode: i2b (implicit 's2i') jaroslav@1646: return (byte)x; jaroslav@1646: } jaroslav@1646: static boolean shortToBoolean(short x) { jaroslav@1646: return toBoolean((byte) x); jaroslav@1646: } jaroslav@1646: jaroslav@1646: // widen char: jaroslav@1646: static double charToDouble(char x) { // bytecode: i2d (implicit 'c2i') jaroslav@1646: return x; jaroslav@1646: } jaroslav@1646: static float charToFloat(char x) { // bytecode: i2f (implicit 'c2i') jaroslav@1646: return x; jaroslav@1646: } jaroslav@1646: static long charToLong(char x) { // bytecode: i2l (implicit 'c2i') jaroslav@1646: return x; jaroslav@1646: } jaroslav@1646: static int charToInt(char x) { // (implicit 'c2i') jaroslav@1646: return x; jaroslav@1646: } jaroslav@1646: // narrow char: jaroslav@1646: static short charToShort(char x) { // bytecode: i2s (implicit 'c2i') jaroslav@1646: return (short)x; jaroslav@1646: } jaroslav@1646: static byte charToByte(char x) { // bytecode: i2b (implicit 'c2i') jaroslav@1646: return (byte)x; jaroslav@1646: } jaroslav@1646: static boolean charToBoolean(char x) { jaroslav@1646: return toBoolean((byte) x); jaroslav@1646: } jaroslav@1646: jaroslav@1646: // widen byte: jaroslav@1646: static double byteToDouble(byte x) { // bytecode: i2d (implicit 'b2i') jaroslav@1646: return x; jaroslav@1646: } jaroslav@1646: static float byteToFloat(byte x) { // bytecode: i2f (implicit 'b2i') jaroslav@1646: return x; jaroslav@1646: } jaroslav@1646: static long byteToLong(byte x) { // bytecode: i2l (implicit 'b2i') jaroslav@1646: return x; jaroslav@1646: } jaroslav@1646: static int byteToInt(byte x) { // (implicit 'b2i') jaroslav@1646: return x; jaroslav@1646: } jaroslav@1646: static short byteToShort(byte x) { // bytecode: i2s (implicit 'b2i') jaroslav@1646: return (short)x; jaroslav@1646: } jaroslav@1646: static char byteToChar(byte x) { // bytecode: i2b (implicit 'b2i') jaroslav@1646: return (char)x; jaroslav@1646: } jaroslav@1646: // narrow byte to boolean: jaroslav@1646: static boolean byteToBoolean(byte x) { jaroslav@1646: return toBoolean(x); jaroslav@1646: } jaroslav@1646: jaroslav@1646: // widen boolean to all types: jaroslav@1646: static double booleanToDouble(boolean x) { jaroslav@1646: return fromBoolean(x); jaroslav@1646: } jaroslav@1646: static float booleanToFloat(boolean x) { jaroslav@1646: return fromBoolean(x); jaroslav@1646: } jaroslav@1646: static long booleanToLong(boolean x) { jaroslav@1646: return fromBoolean(x); jaroslav@1646: } jaroslav@1646: static int booleanToInt(boolean x) { jaroslav@1646: return fromBoolean(x); jaroslav@1646: } jaroslav@1646: static short booleanToShort(boolean x) { jaroslav@1646: return fromBoolean(x); jaroslav@1646: } jaroslav@1646: static char booleanToChar(boolean x) { jaroslav@1646: return (char)fromBoolean(x); jaroslav@1646: } jaroslav@1646: static byte booleanToByte(boolean x) { jaroslav@1646: return fromBoolean(x); jaroslav@1646: } jaroslav@1646: jaroslav@1646: // helpers to force boolean into the conversion scheme: jaroslav@1646: static boolean toBoolean(byte x) { jaroslav@1646: // see javadoc for MethodHandles.explicitCastArguments jaroslav@1646: return ((x & 1) != 0); jaroslav@1646: } jaroslav@1646: static byte fromBoolean(boolean x) { jaroslav@1646: // see javadoc for MethodHandles.explicitCastArguments jaroslav@1646: return (x ? (byte)1 : (byte)0); jaroslav@1646: } jaroslav@1646: jaroslav@1646: private static final EnumMap[] jaroslav@1646: CONVERT_PRIMITIVE_FUNCTIONS = newWrapperCaches(Wrapper.values().length); jaroslav@1646: jaroslav@1646: public static MethodHandle convertPrimitive(Wrapper wsrc, Wrapper wdst) { jaroslav@1646: EnumMap cache = CONVERT_PRIMITIVE_FUNCTIONS[wsrc.ordinal()]; jaroslav@1646: MethodHandle mh = cache.get(wdst); jaroslav@1646: if (mh != null) { jaroslav@1646: return mh; jaroslav@1646: } jaroslav@1646: // slow path jaroslav@1646: Class src = wsrc.primitiveType(); jaroslav@1646: Class dst = wdst.primitiveType(); jaroslav@1646: MethodType type = src == void.class ? MethodType.methodType(dst) : MethodType.methodType(dst, src); jaroslav@1646: if (wsrc == wdst) { jaroslav@1646: mh = identity(src); jaroslav@1646: } else if (wsrc == Wrapper.VOID) { jaroslav@1646: mh = zeroConstantFunction(wdst); jaroslav@1646: } else if (wdst == Wrapper.VOID) { jaroslav@1646: mh = MethodHandles.dropArguments(EMPTY, 0, src); // Defer back to MethodHandles. jaroslav@1646: } else if (wsrc == Wrapper.OBJECT) { jaroslav@1646: mh = unboxCast(dst); jaroslav@1646: } else if (wdst == Wrapper.OBJECT) { jaroslav@1646: mh = box(src); jaroslav@1646: } else { jaroslav@1646: assert(src.isPrimitive() && dst.isPrimitive()); jaroslav@1646: try { jaroslav@1646: mh = IMPL_LOOKUP.findStatic(THIS_CLASS, src.getSimpleName()+"To"+capitalize(dst.getSimpleName()), type); jaroslav@1646: } catch (ReflectiveOperationException ex) { jaroslav@1646: mh = null; jaroslav@1646: } jaroslav@1646: } jaroslav@1646: if (mh != null) { jaroslav@1646: assert(mh.type() == type) : mh; jaroslav@1646: cache.put(wdst, mh); jaroslav@1646: return mh; jaroslav@1646: } jaroslav@1646: jaroslav@1646: throw new IllegalArgumentException("cannot find primitive conversion function for " + jaroslav@1646: src.getSimpleName()+" -> "+dst.getSimpleName()); jaroslav@1646: } jaroslav@1646: jaroslav@1646: public static MethodHandle convertPrimitive(Class src, Class dst) { jaroslav@1646: return convertPrimitive(Wrapper.forPrimitiveType(src), Wrapper.forPrimitiveType(dst)); jaroslav@1646: } jaroslav@1646: jaroslav@1646: private static String capitalize(String x) { jaroslav@1646: return Character.toUpperCase(x.charAt(0))+x.substring(1); jaroslav@1646: } jaroslav@1646: jaroslav@1646: /// Collection of multiple arguments. jaroslav@1646: jaroslav@1646: public static Object convertArrayElements(Class arrayType, Object array) { jaroslav@1646: Class src = array.getClass().getComponentType(); jaroslav@1646: Class dst = arrayType.getComponentType(); jaroslav@1646: if (src == null || dst == null) throw new IllegalArgumentException("not array type"); jaroslav@1646: Wrapper sw = (src.isPrimitive() ? Wrapper.forPrimitiveType(src) : null); jaroslav@1646: Wrapper dw = (dst.isPrimitive() ? Wrapper.forPrimitiveType(dst) : null); jaroslav@1646: int length; jaroslav@1646: if (sw == null) { jaroslav@1646: Object[] a = (Object[]) array; jaroslav@1646: length = a.length; jaroslav@1646: if (dw == null) jaroslav@1646: return Arrays.copyOf(a, length, arrayType.asSubclass(Object[].class)); jaroslav@1646: Object res = dw.makeArray(length); jaroslav@1646: dw.copyArrayUnboxing(a, 0, res, 0, length); jaroslav@1646: return res; jaroslav@1646: } jaroslav@1646: length = java.lang.reflect.Array.getLength(array); jaroslav@1646: Object[] res; jaroslav@1646: if (dw == null) { jaroslav@1646: res = Arrays.copyOf(NO_ARGS_ARRAY, length, arrayType.asSubclass(Object[].class)); jaroslav@1646: } else { jaroslav@1646: res = new Object[length]; jaroslav@1646: } jaroslav@1646: sw.copyArrayBoxing(array, 0, res, 0, length); jaroslav@1646: if (dw == null) return res; jaroslav@1646: Object a = dw.makeArray(length); jaroslav@1646: dw.copyArrayUnboxing(res, 0, a, 0, length); jaroslav@1646: return a; jaroslav@1646: } jaroslav@1646: jaroslav@1646: private static MethodHandle findCollector(String name, int nargs, Class rtype, Class... ptypes) { jaroslav@1646: MethodType type = MethodType.genericMethodType(nargs) jaroslav@1646: .changeReturnType(rtype) jaroslav@1646: .insertParameterTypes(0, ptypes); jaroslav@1646: try { jaroslav@1646: return IMPL_LOOKUP.findStatic(THIS_CLASS, name, type); jaroslav@1646: } catch (ReflectiveOperationException ex) { jaroslav@1646: return null; jaroslav@1646: } jaroslav@1646: } jaroslav@1646: jaroslav@1646: private static final Object[] NO_ARGS_ARRAY = {}; jaroslav@1646: private static Object[] makeArray(Object... args) { return args; } jaroslav@1646: private static Object[] array() { return NO_ARGS_ARRAY; } jaroslav@1646: private static Object[] array(Object a0) jaroslav@1646: { return makeArray(a0); } jaroslav@1646: private static Object[] array(Object a0, Object a1) jaroslav@1646: { return makeArray(a0, a1); } jaroslav@1646: private static Object[] array(Object a0, Object a1, Object a2) jaroslav@1646: { return makeArray(a0, a1, a2); } jaroslav@1646: private static Object[] array(Object a0, Object a1, Object a2, Object a3) jaroslav@1646: { return makeArray(a0, a1, a2, a3); } jaroslav@1646: private static Object[] array(Object a0, Object a1, Object a2, Object a3, jaroslav@1646: Object a4) jaroslav@1646: { return makeArray(a0, a1, a2, a3, a4); } jaroslav@1646: private static Object[] array(Object a0, Object a1, Object a2, Object a3, jaroslav@1646: Object a4, Object a5) jaroslav@1646: { return makeArray(a0, a1, a2, a3, a4, a5); } jaroslav@1646: private static Object[] array(Object a0, Object a1, Object a2, Object a3, jaroslav@1646: Object a4, Object a5, Object a6) jaroslav@1646: { return makeArray(a0, a1, a2, a3, a4, a5, a6); } jaroslav@1646: private static Object[] array(Object a0, Object a1, Object a2, Object a3, jaroslav@1646: Object a4, Object a5, Object a6, Object a7) jaroslav@1646: { return makeArray(a0, a1, a2, a3, a4, a5, a6, a7); } jaroslav@1646: private static Object[] array(Object a0, Object a1, Object a2, Object a3, jaroslav@1646: Object a4, Object a5, Object a6, Object a7, jaroslav@1646: Object a8) jaroslav@1646: { return makeArray(a0, a1, a2, a3, a4, a5, a6, a7, a8); } jaroslav@1646: private static Object[] array(Object a0, Object a1, Object a2, Object a3, jaroslav@1646: Object a4, Object a5, Object a6, Object a7, jaroslav@1646: Object a8, Object a9) jaroslav@1646: { return makeArray(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); } jaroslav@1646: private static MethodHandle[] makeArrays() { jaroslav@1646: ArrayList mhs = new ArrayList<>(); jaroslav@1646: for (;;) { jaroslav@1646: MethodHandle mh = findCollector("array", mhs.size(), Object[].class); jaroslav@1646: if (mh == null) break; jaroslav@1646: mhs.add(mh); jaroslav@1646: } jaroslav@1646: assert(mhs.size() == 11); // current number of methods jaroslav@1646: return mhs.toArray(new MethodHandle[MAX_ARITY+1]); jaroslav@1646: } jaroslav@1646: private static final MethodHandle[] ARRAYS = makeArrays(); jaroslav@1646: jaroslav@1646: // filling versions of the above: jaroslav@1646: // using Integer len instead of int len and no varargs to avoid bootstrapping problems jaroslav@1646: private static Object[] fillNewArray(Integer len, Object[] /*not ...*/ args) { jaroslav@1646: Object[] a = new Object[len]; jaroslav@1646: fillWithArguments(a, 0, args); jaroslav@1646: return a; jaroslav@1646: } jaroslav@1646: private static Object[] fillNewTypedArray(Object[] example, Integer len, Object[] /*not ...*/ args) { jaroslav@1646: Object[] a = Arrays.copyOf(example, len); jaroslav@1646: fillWithArguments(a, 0, args); jaroslav@1646: return a; jaroslav@1646: } jaroslav@1646: private static void fillWithArguments(Object[] a, int pos, Object... args) { jaroslav@1646: System.arraycopy(args, 0, a, pos, args.length); jaroslav@1646: } jaroslav@1646: // using Integer pos instead of int pos to avoid bootstrapping problems jaroslav@1646: private static Object[] fillArray(Integer pos, Object[] a, Object a0) jaroslav@1646: { fillWithArguments(a, pos, a0); return a; } jaroslav@1646: private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1) jaroslav@1646: { fillWithArguments(a, pos, a0, a1); return a; } jaroslav@1646: private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2) jaroslav@1646: { fillWithArguments(a, pos, a0, a1, a2); return a; } jaroslav@1646: private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3) jaroslav@1646: { fillWithArguments(a, pos, a0, a1, a2, a3); return a; } jaroslav@1646: private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3, jaroslav@1646: Object a4) jaroslav@1646: { fillWithArguments(a, pos, a0, a1, a2, a3, a4); return a; } jaroslav@1646: private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3, jaroslav@1646: Object a4, Object a5) jaroslav@1646: { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5); return a; } jaroslav@1646: private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3, jaroslav@1646: Object a4, Object a5, Object a6) jaroslav@1646: { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6); return a; } jaroslav@1646: private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3, jaroslav@1646: Object a4, Object a5, Object a6, Object a7) jaroslav@1646: { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7); return a; } jaroslav@1646: private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3, jaroslav@1646: Object a4, Object a5, Object a6, Object a7, jaroslav@1646: Object a8) jaroslav@1646: { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7, a8); return a; } jaroslav@1646: private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3, jaroslav@1646: Object a4, Object a5, Object a6, Object a7, jaroslav@1646: Object a8, Object a9) jaroslav@1646: { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); return a; } jaroslav@1646: private static MethodHandle[] makeFillArrays() { jaroslav@1646: ArrayList mhs = new ArrayList<>(); jaroslav@1646: mhs.add(null); // there is no empty fill; at least a0 is required jaroslav@1646: for (;;) { jaroslav@1646: MethodHandle mh = findCollector("fillArray", mhs.size(), Object[].class, Integer.class, Object[].class); jaroslav@1646: if (mh == null) break; jaroslav@1646: mhs.add(mh); jaroslav@1646: } jaroslav@1646: assert(mhs.size() == 11); // current number of methods jaroslav@1646: return mhs.toArray(new MethodHandle[0]); jaroslav@1646: } jaroslav@1646: private static final MethodHandle[] FILL_ARRAYS = makeFillArrays(); jaroslav@1646: jaroslav@1646: private static Object[] copyAsReferenceArray(Class arrayType, Object... a) { jaroslav@1646: return Arrays.copyOf(a, a.length, arrayType); jaroslav@1646: } jaroslav@1646: private static Object copyAsPrimitiveArray(Wrapper w, Object... boxes) { jaroslav@1646: Object a = w.makeArray(boxes.length); jaroslav@1646: w.copyArrayUnboxing(boxes, 0, a, 0, boxes.length); jaroslav@1646: return a; jaroslav@1646: } jaroslav@1646: jaroslav@1646: /** Return a method handle that takes the indicated number of Object jaroslav@1646: * arguments and returns an Object array of them, as if for varargs. jaroslav@1646: */ jaroslav@1646: public static MethodHandle varargsArray(int nargs) { jaroslav@1646: MethodHandle mh = ARRAYS[nargs]; jaroslav@1646: if (mh != null) return mh; jaroslav@1646: mh = findCollector("array", nargs, Object[].class); jaroslav@1646: if (mh != null) return ARRAYS[nargs] = mh; jaroslav@1646: mh = buildVarargsArray(FILL_NEW_ARRAY, ARRAY_IDENTITY, nargs); jaroslav@1646: assert(assertCorrectArity(mh, nargs)); jaroslav@1646: return ARRAYS[nargs] = mh; jaroslav@1646: } jaroslav@1646: jaroslav@1646: private static boolean assertCorrectArity(MethodHandle mh, int arity) { jaroslav@1646: assert(mh.type().parameterCount() == arity) : "arity != "+arity+": "+mh; jaroslav@1646: return true; jaroslav@1646: } jaroslav@1646: jaroslav@1646: private static MethodHandle buildVarargsArray(MethodHandle newArray, MethodHandle finisher, int nargs) { jaroslav@1646: // Build up the result mh as a sequence of fills like this: jaroslav@1646: // finisher(fill(fill(newArrayWA(23,x1..x10),10,x11..x20),20,x21..x23)) jaroslav@1646: // The various fill(_,10*I,___*[J]) are reusable. jaroslav@1646: int leftLen = Math.min(nargs, LEFT_ARGS); // absorb some arguments immediately jaroslav@1646: int rightLen = nargs - leftLen; jaroslav@1646: MethodHandle leftCollector = newArray.bindTo(nargs); jaroslav@1646: leftCollector = leftCollector.asCollector(Object[].class, leftLen); jaroslav@1646: MethodHandle mh = finisher; jaroslav@1646: if (rightLen > 0) { jaroslav@1646: MethodHandle rightFiller = fillToRight(LEFT_ARGS + rightLen); jaroslav@1646: if (mh == ARRAY_IDENTITY) jaroslav@1646: mh = rightFiller; jaroslav@1646: else jaroslav@1646: mh = MethodHandles.collectArguments(mh, 0, rightFiller); jaroslav@1646: } jaroslav@1646: if (mh == ARRAY_IDENTITY) jaroslav@1646: mh = leftCollector; jaroslav@1646: else jaroslav@1646: mh = MethodHandles.collectArguments(mh, 0, leftCollector); jaroslav@1646: return mh; jaroslav@1646: } jaroslav@1646: jaroslav@1646: private static final int LEFT_ARGS = (FILL_ARRAYS.length - 1); jaroslav@1646: private static final MethodHandle[] FILL_ARRAY_TO_RIGHT = new MethodHandle[MAX_ARITY+1]; jaroslav@1646: /** fill_array_to_right(N).invoke(a, argL..arg[N-1]) jaroslav@1646: * fills a[L]..a[N-1] with corresponding arguments, jaroslav@1646: * and then returns a. The value L is a global constant (LEFT_ARGS). jaroslav@1646: */ jaroslav@1646: private static MethodHandle fillToRight(int nargs) { jaroslav@1646: MethodHandle filler = FILL_ARRAY_TO_RIGHT[nargs]; jaroslav@1646: if (filler != null) return filler; jaroslav@1646: filler = buildFiller(nargs); jaroslav@1646: assert(assertCorrectArity(filler, nargs - LEFT_ARGS + 1)); jaroslav@1646: return FILL_ARRAY_TO_RIGHT[nargs] = filler; jaroslav@1646: } jaroslav@1646: private static MethodHandle buildFiller(int nargs) { jaroslav@1646: if (nargs <= LEFT_ARGS) jaroslav@1646: return ARRAY_IDENTITY; // no args to fill; return the array unchanged jaroslav@1646: // we need room for both mh and a in mh.invoke(a, arg*[nargs]) jaroslav@1646: final int CHUNK = LEFT_ARGS; jaroslav@1646: int rightLen = nargs % CHUNK; jaroslav@1646: int midLen = nargs - rightLen; jaroslav@1646: if (rightLen == 0) { jaroslav@1646: midLen = nargs - (rightLen = CHUNK); jaroslav@1646: if (FILL_ARRAY_TO_RIGHT[midLen] == null) { jaroslav@1646: // build some precursors from left to right jaroslav@1646: for (int j = LEFT_ARGS % CHUNK; j < midLen; j += CHUNK) jaroslav@1646: if (j > LEFT_ARGS) fillToRight(j); jaroslav@1646: } jaroslav@1646: } jaroslav@1646: if (midLen < LEFT_ARGS) rightLen = nargs - (midLen = LEFT_ARGS); jaroslav@1646: assert(rightLen > 0); jaroslav@1646: MethodHandle midFill = fillToRight(midLen); // recursive fill jaroslav@1646: MethodHandle rightFill = FILL_ARRAYS[rightLen].bindTo(midLen); // [midLen..nargs-1] jaroslav@1646: assert(midFill.type().parameterCount() == 1 + midLen - LEFT_ARGS); jaroslav@1646: assert(rightFill.type().parameterCount() == 1 + rightLen); jaroslav@1646: jaroslav@1646: // Combine the two fills: jaroslav@1646: // right(mid(a, x10..x19), x20..x23) jaroslav@1646: // The final product will look like this: jaroslav@1646: // right(mid(newArrayLeft(24, x0..x9), x10..x19), x20..x23) jaroslav@1646: if (midLen == LEFT_ARGS) jaroslav@1646: return rightFill; jaroslav@1646: else jaroslav@1646: return MethodHandles.collectArguments(rightFill, 0, midFill); jaroslav@1646: } jaroslav@1646: jaroslav@1646: // Type-polymorphic version of varargs maker. jaroslav@1646: private static final ClassValue TYPED_COLLECTORS jaroslav@1646: = new ClassValue() { jaroslav@1646: @Override jaroslav@1646: protected MethodHandle[] computeValue(Class type) { jaroslav@1646: return new MethodHandle[256]; jaroslav@1646: } jaroslav@1646: }; jaroslav@1646: jaroslav@1646: static final int MAX_JVM_ARITY = 255; // limit imposed by the JVM jaroslav@1646: jaroslav@1646: /** Return a method handle that takes the indicated number of jaroslav@1646: * typed arguments and returns an array of them. jaroslav@1646: * The type argument is the array type. jaroslav@1646: */ jaroslav@1646: public static MethodHandle varargsArray(Class arrayType, int nargs) { jaroslav@1646: Class elemType = arrayType.getComponentType(); jaroslav@1646: if (elemType == null) throw new IllegalArgumentException("not an array: "+arrayType); jaroslav@1646: // FIXME: Need more special casing and caching here. jaroslav@1646: if (nargs >= MAX_JVM_ARITY/2 - 1) { jaroslav@1646: int slots = nargs; jaroslav@1646: final int MAX_ARRAY_SLOTS = MAX_JVM_ARITY - 1; // 1 for receiver MH jaroslav@1646: if (arrayType == double[].class || arrayType == long[].class) jaroslav@1646: slots *= 2; jaroslav@1646: if (slots > MAX_ARRAY_SLOTS) jaroslav@1646: throw new IllegalArgumentException("too many arguments: "+arrayType.getSimpleName()+", length "+nargs); jaroslav@1646: } jaroslav@1646: if (elemType == Object.class) jaroslav@1646: return varargsArray(nargs); jaroslav@1646: // other cases: primitive arrays, subtypes of Object[] jaroslav@1646: MethodHandle cache[] = TYPED_COLLECTORS.get(elemType); jaroslav@1646: MethodHandle mh = nargs < cache.length ? cache[nargs] : null; jaroslav@1646: if (mh != null) return mh; jaroslav@1646: if (elemType.isPrimitive()) { jaroslav@1646: MethodHandle builder = FILL_NEW_ARRAY; jaroslav@1646: MethodHandle producer = buildArrayProducer(arrayType); jaroslav@1646: mh = buildVarargsArray(builder, producer, nargs); jaroslav@1646: } else { jaroslav@1646: @SuppressWarnings("unchecked") jaroslav@1646: Class objArrayType = (Class) arrayType; jaroslav@1646: Object[] example = Arrays.copyOf(NO_ARGS_ARRAY, 0, objArrayType); jaroslav@1646: MethodHandle builder = FILL_NEW_TYPED_ARRAY.bindTo(example); jaroslav@1646: MethodHandle producer = ARRAY_IDENTITY; jaroslav@1646: mh = buildVarargsArray(builder, producer, nargs); jaroslav@1646: } jaroslav@1646: mh = mh.asType(MethodType.methodType(arrayType, Collections.>nCopies(nargs, elemType))); jaroslav@1646: assert(assertCorrectArity(mh, nargs)); jaroslav@1646: if (nargs < cache.length) jaroslav@1646: cache[nargs] = mh; jaroslav@1646: return mh; jaroslav@1646: } jaroslav@1646: jaroslav@1646: private static MethodHandle buildArrayProducer(Class arrayType) { jaroslav@1646: Class elemType = arrayType.getComponentType(); jaroslav@1646: if (elemType.isPrimitive()) jaroslav@1646: return LazyStatics.COPY_AS_PRIMITIVE_ARRAY.bindTo(Wrapper.forPrimitiveType(elemType)); jaroslav@1646: else jaroslav@1646: return LazyStatics.COPY_AS_REFERENCE_ARRAY.bindTo(arrayType); jaroslav@1646: } jaroslav@1646: jaroslav@1646: // List version of varargs maker. jaroslav@1646: jaroslav@1646: private static final List NO_ARGS_LIST = Arrays.asList(NO_ARGS_ARRAY); jaroslav@1646: private static List makeList(Object... args) { return Arrays.asList(args); } jaroslav@1646: private static List list() { return NO_ARGS_LIST; } jaroslav@1646: private static List list(Object a0) jaroslav@1646: { return makeList(a0); } jaroslav@1646: private static List list(Object a0, Object a1) jaroslav@1646: { return makeList(a0, a1); } jaroslav@1646: private static List list(Object a0, Object a1, Object a2) jaroslav@1646: { return makeList(a0, a1, a2); } jaroslav@1646: private static List list(Object a0, Object a1, Object a2, Object a3) jaroslav@1646: { return makeList(a0, a1, a2, a3); } jaroslav@1646: private static List list(Object a0, Object a1, Object a2, Object a3, jaroslav@1646: Object a4) jaroslav@1646: { return makeList(a0, a1, a2, a3, a4); } jaroslav@1646: private static List list(Object a0, Object a1, Object a2, Object a3, jaroslav@1646: Object a4, Object a5) jaroslav@1646: { return makeList(a0, a1, a2, a3, a4, a5); } jaroslav@1646: private static List list(Object a0, Object a1, Object a2, Object a3, jaroslav@1646: Object a4, Object a5, Object a6) jaroslav@1646: { return makeList(a0, a1, a2, a3, a4, a5, a6); } jaroslav@1646: private static List list(Object a0, Object a1, Object a2, Object a3, jaroslav@1646: Object a4, Object a5, Object a6, Object a7) jaroslav@1646: { return makeList(a0, a1, a2, a3, a4, a5, a6, a7); } jaroslav@1646: private static List list(Object a0, Object a1, Object a2, Object a3, jaroslav@1646: Object a4, Object a5, Object a6, Object a7, jaroslav@1646: Object a8) jaroslav@1646: { return makeList(a0, a1, a2, a3, a4, a5, a6, a7, a8); } jaroslav@1646: private static List list(Object a0, Object a1, Object a2, Object a3, jaroslav@1646: Object a4, Object a5, Object a6, Object a7, jaroslav@1646: Object a8, Object a9) jaroslav@1646: { return makeList(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); } jaroslav@1646: private static MethodHandle[] makeLists() { jaroslav@1646: ArrayList mhs = new ArrayList<>(); jaroslav@1646: for (;;) { jaroslav@1646: MethodHandle mh = findCollector("list", mhs.size(), List.class); jaroslav@1646: if (mh == null) break; jaroslav@1646: mhs.add(mh); jaroslav@1646: } jaroslav@1646: assert(mhs.size() == 11); // current number of methods jaroslav@1646: return mhs.toArray(new MethodHandle[MAX_ARITY+1]); jaroslav@1646: } jaroslav@1646: private static final MethodHandle[] LISTS = makeLists(); jaroslav@1646: jaroslav@1646: /** Return a method handle that takes the indicated number of Object jaroslav@1646: * arguments and returns a List. jaroslav@1646: */ jaroslav@1646: public static MethodHandle varargsList(int nargs) { jaroslav@1646: MethodHandle mh = LISTS[nargs]; jaroslav@1646: if (mh != null) return mh; jaroslav@1646: mh = findCollector("list", nargs, List.class); jaroslav@1646: if (mh != null) return LISTS[nargs] = mh; jaroslav@1646: return LISTS[nargs] = buildVarargsList(nargs); jaroslav@1646: } jaroslav@1646: private static MethodHandle buildVarargsList(int nargs) { jaroslav@1646: return MethodHandles.filterReturnValue(varargsArray(nargs), LazyStatics.MAKE_LIST); jaroslav@1646: } jaroslav@1646: jaroslav@1646: // handy shared exception makers (they simplify the common case code) jaroslav@1646: private static InternalError newInternalError(String message, Throwable cause) { jaroslav@1646: return new InternalError(message, cause); jaroslav@1646: } jaroslav@1646: private static InternalError newInternalError(Throwable cause) { jaroslav@1646: return new InternalError(cause); jaroslav@1646: } jaroslav@1646: }