jdk/src/share/classes/sun/dyn/util/ValueConversions.java
changeset 2707 5a17df307cbc
child 4537 7c3c7f8d5195
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/dyn/util/ValueConversions.java	Tue May 05 22:40:09 2009 -0700
@@ -0,0 +1,563 @@
+/*
+ * Copyright 2008-2009 Sun Microsystems, Inc.  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.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.dyn.util;
+
+import java.dyn.*;
+import java.dyn.MethodHandles.Lookup;
+import java.util.EnumMap;
+import sun.dyn.Access;
+import sun.dyn.AdapterMethodHandle;
+import sun.dyn.MethodHandleImpl;
+
+public class ValueConversions {
+    private static final Access IMPL_TOKEN = Access.getToken();
+    private static final Lookup IMPL_LOOKUP = MethodHandleImpl.getLookup(IMPL_TOKEN);
+
+    private static EnumMap<Wrapper, MethodHandle>[] newWrapperCaches(int n) {
+        EnumMap<Wrapper, MethodHandle>[] caches
+                = (EnumMap<Wrapper, MethodHandle>[]) new EnumMap[n];  // unchecked warning expected here
+        for (int i = 0; i < n; i++)
+            caches[i] = new EnumMap<Wrapper, MethodHandle>(Wrapper.class);
+        return caches;
+    }
+
+    /// Converting references to values.
+
+    static int unboxInteger(Object x) {
+        if (x == null)  return 0;  // never NPE
+        return ((Integer) x).intValue();
+    }
+
+    static byte unboxByte(Object x) {
+        if (x == null)  return 0;  // never NPE
+        return ((Byte) x).byteValue();
+    }
+
+    static short unboxShort(Object x) {
+        if (x == null)  return 0;  // never NPE
+        return ((Short) x).shortValue();
+    }
+
+    static boolean unboxBoolean(Object x) {
+        if (x == null)  return false;  // never NPE
+        return ((Boolean) x).booleanValue();
+    }
+
+    static char unboxCharacter(Object x) {
+        if (x == null)  return 0;  // never NPE
+        return ((Character) x).charValue();
+    }
+
+    static long unboxLong(Object x) {
+        if (x == null)  return 0;  // never NPE
+        return ((Long) x).longValue();
+    }
+
+    static float unboxFloat(Object x) {
+        if (x == null)  return 0;  // never NPE
+        return ((Float) x).floatValue();
+    }
+
+    static double unboxDouble(Object x) {
+        if (x == null)  return 0;  // never NPE
+        return ((Double) x).doubleValue();
+    }
+
+    /// Converting references to "raw" values.
+    /// A raw primitive value is always an int or long.
+
+    static int unboxByteRaw(Object x) {
+        return unboxByte(x);
+    }
+
+    static int unboxShortRaw(Object x) {
+        return unboxShort(x);
+    }
+
+    static int unboxBooleanRaw(Object x) {
+        return unboxBoolean(x) ? 1 : 0;
+    }
+
+    static int unboxCharacterRaw(Object x) {
+        return unboxCharacter(x);
+    }
+
+    static int unboxFloatRaw(Object x) {
+        return Float.floatToIntBits(unboxFloat(x));
+    }
+
+    static long unboxDoubleRaw(Object x) {
+        return Double.doubleToRawLongBits(unboxDouble(x));
+    }
+
+    private static MethodType unboxType(Wrapper wrap, boolean raw) {
+        return MethodType.make(rawWrapper(wrap, raw).primitiveType(), wrap.wrapperType());
+    }
+
+    private static final EnumMap<Wrapper, MethodHandle>[]
+            UNBOX_CONVERSIONS = newWrapperCaches(4);
+
+    private static MethodHandle unbox(Wrapper wrap, boolean exact, boolean raw) {
+        EnumMap<Wrapper, MethodHandle> cache = UNBOX_CONVERSIONS[(exact?1:0)+(raw?2:0)];
+        MethodHandle mh = cache.get(wrap);
+        if (mh != null) {
+            return mh;
+        }
+        // slow path
+        switch (wrap) {
+            case OBJECT:
+                mh = IDENTITY; break;
+            case VOID:
+                mh = raw ? ALWAYS_ZERO : IGNORE; break;
+            case INT: case LONG:
+                // these guys don't need separate raw channels
+                if (raw)  mh = unbox(wrap, exact, false);
+                break;
+        }
+        if (mh != null) {
+            cache.put(wrap, mh);
+            return mh;
+        }
+        // look up the method
+        String name = "unbox" + wrap.simpleName() + (raw ? "Raw" : "");
+        MethodType type = unboxType(wrap, raw);
+        if (!exact)
+            // actually, type is wrong; the Java method takes Object
+            mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type.erase());
+        else
+            mh = retype(type, unbox(wrap, !exact, raw));
+        if (mh != null) {
+            cache.put(wrap, mh);
+            return mh;
+        }
+        throw new IllegalArgumentException("cannot find unbox adapter for " + wrap + (raw ? " (raw)" : ""));
+    }
+
+    public static MethodHandle unbox(Wrapper type, boolean exact) {
+        return unbox(type, exact, false);
+    }
+
+    public static MethodHandle unboxRaw(Wrapper type, boolean exact) {
+        return unbox(type, exact, true);
+    }
+
+    public static MethodHandle unbox(Class<?> type, boolean exact) {
+        return unbox(Wrapper.forPrimitiveType(type), exact, false);
+    }
+
+    public static MethodHandle unboxRaw(Class<?> type, boolean exact) {
+        return unbox(Wrapper.forPrimitiveType(type), exact, true);
+    }
+
+    /// 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;
+    }
+
+    /// Converting raw primitives to references
+
+    static Byte boxByteRaw(int x) {
+        return boxByte((byte)x);
+    }
+
+    static Short boxShortRaw(int x) {
+        return boxShort((short)x);
+    }
+
+    static Boolean boxBooleanRaw(int x) {
+        return boxBoolean(x != 0);
+    }
+
+    static Character boxCharacterRaw(int x) {
+        return boxCharacter((char)x);
+    }
+
+    static Float boxFloatRaw(int x) {
+        return boxFloat(Float.intBitsToFloat(x));
+    }
+
+    static Double boxDoubleRaw(long x) {
+        return boxDouble(Double.longBitsToDouble(x));
+    }
+
+    // a raw void value is (arbitrarily) a garbage int
+    static Void boxVoidRaw(int x) {
+        return null;
+    }
+
+    private static MethodType boxType(Wrapper wrap, boolean raw) {
+        // be exact, since return casts are hard to compose
+        Class<?> boxType = wrap.wrapperType();
+        return MethodType.make(boxType, rawWrapper(wrap, raw).primitiveType());
+    }
+
+    private static Wrapper rawWrapper(Wrapper wrap, boolean raw) {
+        if (raw)  return wrap.isDoubleWord() ? Wrapper.LONG : Wrapper.INT;
+        return wrap;
+    }
+
+    private static final EnumMap<Wrapper, MethodHandle>[]
+            BOX_CONVERSIONS = newWrapperCaches(4);
+
+    private static MethodHandle box(Wrapper wrap, boolean exact, boolean raw) {
+        EnumMap<Wrapper, MethodHandle> cache = BOX_CONVERSIONS[(exact?1:0)+(raw?2:0)];
+        MethodHandle mh = cache.get(wrap);
+        if (mh != null) {
+            return mh;
+        }
+        // slow path
+        switch (wrap) {
+            case OBJECT:
+                mh = IDENTITY; break;
+            case VOID:
+                if (!raw)  mh = ZERO_OBJECT;
+                break;
+            case INT: case LONG:
+                // these guys don't need separate raw channels
+                if (raw)  mh = box(wrap, exact, false);
+                break;
+        }
+        if (mh != null) {
+            cache.put(wrap, mh);
+            return mh;
+        }
+        // look up the method
+        String name = "box" + wrap.simpleName() + (raw ? "Raw" : "");
+        MethodType type = boxType(wrap, raw);
+        if (exact)
+            mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type);
+        else
+            mh = retype(type.erase(), box(wrap, !exact, raw));
+        if (mh != null) {
+            cache.put(wrap, mh);
+            return mh;
+        }
+        throw new IllegalArgumentException("cannot find box adapter for " + wrap + (raw ? " (raw)" : ""));
+    }
+
+    public static MethodHandle box(Class<?> type, boolean exact) {
+        return box(Wrapper.forPrimitiveType(type), exact, false);
+    }
+
+    public static MethodHandle boxRaw(Class<?> type, boolean exact) {
+        return box(Wrapper.forPrimitiveType(type), exact, true);
+    }
+
+    public static MethodHandle box(Wrapper type, boolean exact) {
+        return box(type, exact, false);
+    }
+
+    public static MethodHandle boxRaw(Wrapper type, boolean exact) {
+        return box(type, exact, true);
+    }
+
+    /// Kludges for when raw values get accidentally boxed.
+
+    static Byte reboxRawByte(Object x) {
+        if (x instanceof Byte)  return (Byte) x;
+        return boxByteRaw(unboxInteger(x));
+    }
+
+    static Short reboxRawShort(Object x) {
+        if (x instanceof Short)  return (Short) x;
+        return boxShortRaw(unboxInteger(x));
+    }
+
+    static Boolean reboxRawBoolean(Object x) {
+        if (x instanceof Boolean)  return (Boolean) x;
+        return boxBooleanRaw(unboxInteger(x));
+    }
+
+    static Character reboxRawCharacter(Object x) {
+        if (x instanceof Character)  return (Character) x;
+        return boxCharacterRaw(unboxInteger(x));
+    }
+
+    static Float reboxRawFloat(Object x) {
+        if (x instanceof Float)  return (Float) x;
+        return boxFloatRaw(unboxInteger(x));
+    }
+
+    static Double reboxRawDouble(Object x) {
+        if (x instanceof Double)  return (Double) x;
+        return boxDoubleRaw(unboxLong(x));
+    }
+
+    private static MethodType reboxType(Wrapper wrap) {
+        Class<?> boxType = wrap.wrapperType();
+        return MethodType.make(boxType, Object.class);
+    }
+
+    private static final EnumMap<Wrapper, MethodHandle>[]
+            REBOX_CONVERSIONS = newWrapperCaches(2);
+
+    public static MethodHandle rebox(Wrapper wrap, boolean exact) {
+        EnumMap<Wrapper, MethodHandle> cache = REBOX_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:
+                throw new IllegalArgumentException("cannot rebox a void");
+            case INT: case LONG:
+                mh = cast(wrap.wrapperType(), exact);
+                break;
+        }
+        if (mh != null) {
+            cache.put(wrap, mh);
+            return mh;
+        }
+        // look up the method
+        String name = "reboxRaw" + wrap.simpleName();
+        MethodType type = reboxType(wrap);
+        if (exact)
+            mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type);
+        else
+            mh = retype(IDENTITY.type(), rebox(wrap, !exact));
+        if (mh != null) {
+            cache.put(wrap, mh);
+            return mh;
+        }
+        throw new IllegalArgumentException("cannot find rebox adapter for " + wrap);
+    }
+
+    public static MethodHandle rebox(Class<?> type, boolean exact) {
+        return rebox(Wrapper.forPrimitiveType(type), exact);
+    }
+
+    /// Width-changing conversions between int and long.
+
+    static long widenInt(int x) {
+        return x;
+    }
+
+    static int narrowLong(long x) {
+        return (int) x;
+    }
+
+    /// Constant functions
+
+    static void ignore(Object x) {
+        // no value to return; this is an unbox of null
+        return;
+    }
+
+    static void empty() {
+        return;
+    }
+
+    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<Wrapper, MethodHandle>[]
+            ZERO_CONSTANT_FUNCTIONS = newWrapperCaches(1);
+
+    public static MethodHandle zeroConstantFunction(Wrapper wrap) {
+        EnumMap<Wrapper, MethodHandle> cache = ZERO_CONSTANT_FUNCTIONS[0];
+        MethodHandle mh = cache.get(wrap);
+        if (mh != null) {
+            return mh;
+        }
+        // slow path
+        MethodType type = MethodType.make(wrap.primitiveType());
+        switch (wrap) {
+            case VOID:
+                mh = EMPTY;
+                break;
+            case INT: case LONG: case FLOAT: case DOUBLE:
+                mh = IMPL_LOOKUP.findStatic(ValueConversions.class, "zero"+wrap.simpleName(), type);
+                break;
+        }
+        if (mh != null) {
+            cache.put(wrap, mh);
+            return mh;
+        }
+
+        // use the raw method
+        Wrapper rawWrap = wrap.rawPrimitive();
+        if (rawWrap != wrap) {
+            mh = retype(type, zeroConstantFunction(rawWrap));
+        }
+        if (mh != null) {
+            cache.put(wrap, mh);
+            return mh;
+        }
+        throw new IllegalArgumentException("cannot find zero constant for " + wrap);
+    }
+
+    /// Converting references to references.
+
+    /**
+     * Value-killing function.
+     * @param x an arbitrary reference value
+     * @return a null
+     */
+    static Object alwaysNull(Object x) {
+        return null;
+    }
+
+    /**
+     * Value-killing function.
+     * @param x an arbitrary reference value
+     * @return a zero
+     */
+    static int alwaysZero(Object x) {
+        return 0;
+    }
+
+    /**
+     * Identity function.
+     * @param x an arbitrary reference value
+     * @return the same value x
+     */
+    static <T> T identity(T 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
+     */
+    static <T,U> T castReference(Class<? extends T> t, U x) {
+        return t.cast(x);
+    }
+
+    private static final MethodHandle IDENTITY, CAST_REFERENCE, ALWAYS_NULL, ALWAYS_ZERO, ZERO_OBJECT, IGNORE, EMPTY;
+    static {
+        try {
+            MethodType idType = MethodType.makeGeneric(1);
+            MethodType castType = idType.insertParameterType(0, Class.class);
+            MethodType alwaysZeroType = idType.changeReturnType(int.class);
+            MethodType ignoreType = idType.changeReturnType(void.class);
+            MethodType zeroObjectType = MethodType.makeGeneric(0);
+            IDENTITY = IMPL_LOOKUP.findStatic(ValueConversions.class, "identity", idType);
+            //CAST_REFERENCE = IMPL_LOOKUP.findVirtual(Class.class, "cast", idType);
+            CAST_REFERENCE = IMPL_LOOKUP.findStatic(ValueConversions.class, "castReference", castType);
+            ALWAYS_NULL = IMPL_LOOKUP.findStatic(ValueConversions.class, "alwaysNull", idType);
+            ALWAYS_ZERO = IMPL_LOOKUP.findStatic(ValueConversions.class, "alwaysZero", alwaysZeroType);
+            ZERO_OBJECT = IMPL_LOOKUP.findStatic(ValueConversions.class, "zeroObject", zeroObjectType);
+            IGNORE = IMPL_LOOKUP.findStatic(ValueConversions.class, "ignore", ignoreType);
+            EMPTY = IMPL_LOOKUP.findStatic(ValueConversions.class, "empty", ignoreType.dropParameterType(0));
+        } catch (RuntimeException ex) {
+            throw ex;
+        }
+    }
+
+    private static final EnumMap<Wrapper, MethodHandle> WRAPPER_CASTS
+            = new EnumMap<Wrapper, MethodHandle>(Wrapper.class);
+
+    private static final EnumMap<Wrapper, MethodHandle> EXACT_WRAPPER_CASTS
+            = new EnumMap<Wrapper, MethodHandle>(Wrapper.class);
+
+    /** Return a method that casts its sole argument (an Object) to the given type
+     *  and returns it as the given type (if exact is true), or as plain Object (if erase is true).
+     */
+    public static MethodHandle cast(Class<?> type, boolean exact) {
+        if (type.isPrimitive())  throw new IllegalArgumentException("cannot cast primitive type "+type);
+        MethodHandle mh = null;
+        Wrapper wrap = null;
+        EnumMap<Wrapper, MethodHandle> cache = null;
+        if (Wrapper.isWrapperType(type)) {
+            wrap = Wrapper.forWrapperType(type);
+            cache = (exact ? EXACT_WRAPPER_CASTS : WRAPPER_CASTS);
+            mh = cache.get(wrap);
+            if (mh != null)  return mh;
+        }
+        if (VerifyType.isNullReferenceConversion(Object.class, type))
+            mh = IDENTITY;
+        else if (VerifyType.isNullType(type))
+            mh = ALWAYS_NULL;
+        else
+            mh = MethodHandles.insertArgument(CAST_REFERENCE, 0, type);
+        if (exact) {
+            MethodType xmt = MethodType.make(type, Object.class);
+            mh = AdapterMethodHandle.makeRawRetypeOnly(IMPL_TOKEN, xmt, mh);
+        }
+        if (cache != null)
+            cache.put(wrap, mh);
+        return mh;
+    }
+
+    public static MethodHandle identity() {
+        return IDENTITY;
+    }
+
+    private static MethodHandle retype(MethodType type, MethodHandle mh) {
+        return AdapterMethodHandle.makeRetypeOnly(IMPL_TOKEN, type, mh);
+    }
+}