|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
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.util.EnumMap; |
|
|
|
public class ValueConversions { |
|
private static final Class<?> THIS_CLASS = ValueConversions.class; |
|
private static final Lookup IMPL_LOOKUP = MethodHandles.lookup(); |
|
|
|
|
|
|
|
* It's safe to publish MethodHandles by data race because they are immutable. */ |
|
private static class WrapperCache { |
|
|
|
private final EnumMap<Wrapper, MethodHandle> map = new EnumMap<>(Wrapper.class); |
|
|
|
public MethodHandle get(Wrapper w) { |
|
return map.get(w); |
|
} |
|
public synchronized MethodHandle put(final Wrapper w, final MethodHandle mh) { |
|
|
|
MethodHandle prev = map.putIfAbsent(w, mh); |
|
if (prev != null) return prev; |
|
return mh; |
|
} |
|
} |
|
|
|
private static WrapperCache[] newWrapperCaches(int n) { |
|
WrapperCache[] caches = new WrapperCache[n]; |
|
for (int i = 0; i < n; i++) |
|
caches[i] = new WrapperCache(); |
|
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(Integer x) { |
|
return x; |
|
} |
|
static int unboxInteger(Object x, boolean cast) { |
|
if (x instanceof Integer) |
|
return (Integer) x; |
|
return primitiveConversion(Wrapper.INT, x, cast).intValue(); |
|
} |
|
|
|
static byte unboxByte(Byte x) { |
|
return x; |
|
} |
|
static byte unboxByte(Object x, boolean cast) { |
|
if (x instanceof Byte) |
|
return (Byte) x; |
|
return primitiveConversion(Wrapper.BYTE, x, cast).byteValue(); |
|
} |
|
|
|
static short unboxShort(Short x) { |
|
return x; |
|
} |
|
static short unboxShort(Object x, boolean cast) { |
|
if (x instanceof Short) |
|
return (Short) x; |
|
return primitiveConversion(Wrapper.SHORT, x, cast).shortValue(); |
|
} |
|
|
|
static boolean unboxBoolean(Boolean x) { |
|
return x; |
|
} |
|
static boolean unboxBoolean(Object x, boolean cast) { |
|
if (x instanceof Boolean) |
|
return (Boolean) x; |
|
return (primitiveConversion(Wrapper.BOOLEAN, x, cast).intValue() & 1) != 0; |
|
} |
|
|
|
static char unboxCharacter(Character x) { |
|
return x; |
|
} |
|
static char unboxCharacter(Object x, boolean cast) { |
|
if (x instanceof Character) |
|
return (Character) x; |
|
return (char) primitiveConversion(Wrapper.CHAR, x, cast).intValue(); |
|
} |
|
|
|
static long unboxLong(Long x) { |
|
return x; |
|
} |
|
static long unboxLong(Object x, boolean cast) { |
|
if (x instanceof Long) |
|
return (Long) x; |
|
return primitiveConversion(Wrapper.LONG, x, cast).longValue(); |
|
} |
|
|
|
static float unboxFloat(Float x) { |
|
return x; |
|
} |
|
static float unboxFloat(Object x, boolean cast) { |
|
if (x instanceof Float) |
|
return (Float) x; |
|
return primitiveConversion(Wrapper.FLOAT, x, cast).floatValue(); |
|
} |
|
|
|
static double unboxDouble(Double x) { |
|
return x; |
|
} |
|
static double unboxDouble(Object x, boolean cast) { |
|
if (x instanceof Double) |
|
return (Double) x; |
|
return primitiveConversion(Wrapper.DOUBLE, x, cast).doubleValue(); |
|
} |
|
|
|
private static MethodType unboxType(Wrapper wrap, int kind) { |
|
if (kind == 0) |
|
return MethodType.methodType(wrap.primitiveType(), wrap.wrapperType()); |
|
return MethodType.methodType(wrap.primitiveType(), Object.class, boolean.class); |
|
} |
|
|
|
private static final WrapperCache[] UNBOX_CONVERSIONS = newWrapperCaches(4); |
|
|
|
private static MethodHandle unbox(Wrapper wrap, int kind) { |
|
// kind 0 -> strongly typed with NPE |
|
// kind 1 -> strongly typed but zero for null, |
|
// kind 2 -> asType rules: accept multiple box types but only widening conversions with NPE |
|
|
|
WrapperCache cache = UNBOX_CONVERSIONS[kind]; |
|
MethodHandle mh = cache.get(wrap); |
|
if (mh != null) { |
|
return mh; |
|
} |
|
|
|
switch (wrap) { |
|
case OBJECT: |
|
case VOID: |
|
throw new IllegalArgumentException("unbox "+wrap); |
|
} |
|
|
|
String name = "unbox" + wrap.wrapperSimpleName(); |
|
MethodType type = unboxType(wrap, kind); |
|
try { |
|
mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type); |
|
} catch (ReflectiveOperationException ex) { |
|
mh = null; |
|
} |
|
if (mh != null) { |
|
if (kind > 0) { |
|
boolean cast = (kind != 2); |
|
mh = MethodHandles.insertArguments(mh, 1, cast); |
|
} |
|
if (kind == 1) { |
|
mh = mh.asType(unboxType(wrap, 0)); |
|
} |
|
return cache.put(wrap, mh); |
|
} |
|
throw new IllegalArgumentException("cannot find unbox adapter for " + wrap |
|
+ (kind <= 1 ? " (exact)" : kind == 3 ? " (cast)" : "")); |
|
} |
|
|
|
|
|
public static MethodHandle unboxExact(Wrapper type) { |
|
return unbox(type, 0); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public static MethodHandle unboxExact(Wrapper type, boolean throwNPE) { |
|
return unbox(type, throwNPE ? 0 : 1); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static MethodHandle unboxWiden(Wrapper type) { |
|
return unbox(type, 2); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public static MethodHandle unboxCast(Wrapper type) { |
|
return unbox(type, 3); |
|
} |
|
|
|
static private final Integer ZERO_INT = 0, ONE_INT = 1; |
|
|
|
/// Primitive conversions |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static Number primitiveConversion(Wrapper wrap, Object x, boolean 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 { |
|
|
|
res = (Number) x; |
|
} |
|
Wrapper xwrap = Wrapper.findWrapperType(x.getClass()); |
|
if (xwrap == null || !cast && !wrap.isConvertibleFrom(xwrap)) |
|
|
|
return (Number) wrap.wrapperType().cast(x); |
|
return res; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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 |
|
|
|
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) { |
|
|
|
Class<?> boxType = wrap.wrapperType(); |
|
return MethodType.methodType(boxType, wrap.primitiveType()); |
|
} |
|
|
|
private static final WrapperCache[] BOX_CONVERSIONS = newWrapperCaches(1); |
|
|
|
public static MethodHandle boxExact(Wrapper wrap) { |
|
WrapperCache cache = BOX_CONVERSIONS[0]; |
|
MethodHandle mh = cache.get(wrap); |
|
if (mh != null) { |
|
return mh; |
|
} |
|
|
|
String name = "box" + wrap.wrapperSimpleName(); |
|
MethodType type = boxType(wrap); |
|
try { |
|
mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type); |
|
} catch (ReflectiveOperationException ex) { |
|
mh = null; |
|
} |
|
if (mh != null) { |
|
return cache.put(wrap, mh); |
|
} |
|
throw new IllegalArgumentException("cannot find box adapter for " + wrap); |
|
} |
|
|
|
/// 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 WrapperCache[] CONSTANT_FUNCTIONS = newWrapperCaches(2); |
|
|
|
public static MethodHandle zeroConstantFunction(Wrapper wrap) { |
|
WrapperCache cache = CONSTANT_FUNCTIONS[0]; |
|
MethodHandle mh = cache.get(wrap); |
|
if (mh != null) { |
|
return mh; |
|
} |
|
|
|
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) { |
|
return cache.put(wrap, mh); |
|
} |
|
|
|
|
|
if (wrap.isSubwordOrInt() && wrap != Wrapper.INT) { |
|
mh = MethodHandles.explicitCastArguments(zeroConstantFunction(Wrapper.INT), type); |
|
return cache.put(wrap, mh); |
|
} |
|
throw new IllegalArgumentException("cannot find zero constant for " + wrap); |
|
} |
|
|
|
private static final MethodHandle CAST_REFERENCE, IGNORE, EMPTY; |
|
static { |
|
try { |
|
MethodType idType = MethodType.genericMethodType(1); |
|
MethodType ignoreType = idType.changeReturnType(void.class); |
|
CAST_REFERENCE = IMPL_LOOKUP.findVirtual(Class.class, "cast", idType); |
|
IGNORE = IMPL_LOOKUP.findStatic(THIS_CLASS, "ignore", ignoreType); |
|
EMPTY = IMPL_LOOKUP.findStatic(THIS_CLASS, "empty", ignoreType.dropParameterTypes(0, 1)); |
|
} catch (NoSuchMethodException | IllegalAccessException ex) { |
|
throw newInternalError("uncaught exception", ex); |
|
} |
|
} |
|
|
|
public static MethodHandle ignore() { |
|
return IGNORE; |
|
} |
|
|
|
|
|
public static MethodHandle cast() { |
|
return CAST_REFERENCE; |
|
} |
|
|
|
/// 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). |
|
|
|
|
|
static float doubleToFloat(double x) { |
|
return (float) x; |
|
} |
|
static long doubleToLong(double x) { |
|
return (long) x; |
|
} |
|
static int doubleToInt(double x) { |
|
return (int) x; |
|
} |
|
static short doubleToShort(double x) { |
|
return (short) x; |
|
} |
|
static char doubleToChar(double x) { |
|
return (char) x; |
|
} |
|
static byte doubleToByte(double x) { |
|
return (byte) x; |
|
} |
|
static boolean doubleToBoolean(double x) { |
|
return toBoolean((byte) x); |
|
} |
|
|
|
|
|
static double floatToDouble(float x) { |
|
return x; |
|
} |
|
|
|
static long floatToLong(float x) { |
|
return (long) x; |
|
} |
|
static int floatToInt(float x) { |
|
return (int) x; |
|
} |
|
static short floatToShort(float x) { |
|
return (short) x; |
|
} |
|
static char floatToChar(float x) { |
|
return (char) x; |
|
} |
|
static byte floatToByte(float x) { |
|
return (byte) x; |
|
} |
|
static boolean floatToBoolean(float x) { |
|
return toBoolean((byte) x); |
|
} |
|
|
|
|
|
static double longToDouble(long x) { |
|
return x; |
|
} |
|
static float longToFloat(long x) { |
|
return x; |
|
} |
|
|
|
static int longToInt(long x) { |
|
return (int) x; |
|
} |
|
static short longToShort(long x) { |
|
return (short) x; |
|
} |
|
static char longToChar(long x) { |
|
return (char) x; |
|
} |
|
static byte longToByte(long x) { |
|
return (byte) x; |
|
} |
|
static boolean longToBoolean(long x) { |
|
return toBoolean((byte) x); |
|
} |
|
|
|
|
|
static double intToDouble(int x) { |
|
return x; |
|
} |
|
static float intToFloat(int x) { |
|
return x; |
|
} |
|
static long intToLong(int x) { |
|
return x; |
|
} |
|
|
|
static short intToShort(int x) { |
|
return (short) x; |
|
} |
|
static char intToChar(int x) { |
|
return (char) x; |
|
} |
|
static byte intToByte(int x) { |
|
return (byte) x; |
|
} |
|
static boolean intToBoolean(int x) { |
|
return toBoolean((byte) x); |
|
} |
|
|
|
|
|
static double shortToDouble(short x) { |
|
return x; |
|
} |
|
static float shortToFloat(short x) { |
|
return x; |
|
} |
|
static long shortToLong(short x) { |
|
return x; |
|
} |
|
static int shortToInt(short x) { |
|
return x; |
|
} |
|
|
|
static char shortToChar(short x) { |
|
return (char)x; |
|
} |
|
static byte shortToByte(short x) { |
|
return (byte)x; |
|
} |
|
static boolean shortToBoolean(short x) { |
|
return toBoolean((byte) x); |
|
} |
|
|
|
|
|
static double charToDouble(char x) { |
|
return x; |
|
} |
|
static float charToFloat(char x) { |
|
return x; |
|
} |
|
static long charToLong(char x) { |
|
return x; |
|
} |
|
static int charToInt(char x) { |
|
return x; |
|
} |
|
|
|
static short charToShort(char x) { |
|
return (short)x; |
|
} |
|
static byte charToByte(char x) { |
|
return (byte)x; |
|
} |
|
static boolean charToBoolean(char x) { |
|
return toBoolean((byte) x); |
|
} |
|
|
|
|
|
static double byteToDouble(byte x) { |
|
return x; |
|
} |
|
static float byteToFloat(byte x) { |
|
return x; |
|
} |
|
static long byteToLong(byte x) { |
|
return x; |
|
} |
|
static int byteToInt(byte x) { |
|
return x; |
|
} |
|
static short byteToShort(byte x) { |
|
return (short)x; |
|
} |
|
static char byteToChar(byte x) { |
|
return (char)x; |
|
} |
|
|
|
static boolean byteToBoolean(byte x) { |
|
return toBoolean(x); |
|
} |
|
|
|
|
|
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); |
|
} |
|
|
|
|
|
static boolean toBoolean(byte x) { |
|
|
|
return ((x & 1) != 0); |
|
} |
|
static byte fromBoolean(boolean x) { |
|
|
|
return (x ? (byte)1 : (byte)0); |
|
} |
|
|
|
private static final WrapperCache[] CONVERT_PRIMITIVE_FUNCTIONS = newWrapperCaches(Wrapper.values().length); |
|
|
|
public static MethodHandle convertPrimitive(Wrapper wsrc, Wrapper wdst) { |
|
WrapperCache cache = CONVERT_PRIMITIVE_FUNCTIONS[wsrc.ordinal()]; |
|
MethodHandle mh = cache.get(wdst); |
|
if (mh != null) { |
|
return mh; |
|
} |
|
|
|
Class<?> src = wsrc.primitiveType(); |
|
Class<?> dst = wdst.primitiveType(); |
|
MethodType type = MethodType.methodType(dst, src); |
|
if (wsrc == wdst) { |
|
mh = MethodHandles.identity(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; |
|
return cache.put(wdst, 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); |
|
} |
|
|
|
|
|
private static InternalError newInternalError(String message, Throwable cause) { |
|
return new InternalError(message, cause); |
|
} |
|
private static InternalError newInternalError(Throwable cause) { |
|
return new InternalError(cause); |
|
} |
|
} |