Path: blob/master/src/java.base/share/classes/sun/invoke/util/ValueConversions.java
41159 views
/*1* Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.invoke.util;2627import java.lang.invoke.MethodHandle;28import java.lang.invoke.MethodHandles;29import java.lang.invoke.MethodHandles.Lookup;30import java.lang.invoke.MethodType;31import jdk.internal.vm.annotation.Stable;3233public class ValueConversions {34private static final Class<?> THIS_CLASS = ValueConversions.class;35private static final Lookup IMPL_LOOKUP = MethodHandles.lookup();3637/**38* Thread-safe canonicalized mapping from Wrapper to MethodHandle39* with unsynchronized reads and synchronized writes.40* It's safe to publish MethodHandles by data race because they are immutable.41*/42private static class WrapperCache {43@Stable44private final MethodHandle[] map = new MethodHandle[Wrapper.COUNT];4546public MethodHandle get(Wrapper w) {47return map[w.ordinal()];48}49public synchronized MethodHandle put(final Wrapper w, final MethodHandle mh) {50MethodHandle prev = map[w.ordinal()];51if (prev != null) {52return prev;53} else {54map[w.ordinal()] = mh;55return mh;56}57}58}5960private static WrapperCache[] newWrapperCaches(int n) {61WrapperCache[] caches = new WrapperCache[n];62for (int i = 0; i < n; i++)63caches[i] = new WrapperCache();64return caches;65}6667/// Converting references to values.6869// There are several levels of this unboxing conversions:70// no conversions: exactly Integer.valueOf, etc.71// implicit conversions sanctioned by JLS 5.1.2, etc.72// explicit conversions as allowed by explicitCastArguments7374static int unboxInteger(Integer x) {75return x;76}77static int unboxInteger(Object x, boolean cast) {78if (x instanceof Integer)79return (Integer) x;80return primitiveConversion(Wrapper.INT, x, cast).intValue();81}8283static byte unboxByte(Byte x) {84return x;85}86static byte unboxByte(Object x, boolean cast) {87if (x instanceof Byte)88return (Byte) x;89return primitiveConversion(Wrapper.BYTE, x, cast).byteValue();90}9192static short unboxShort(Short x) {93return x;94}95static short unboxShort(Object x, boolean cast) {96if (x instanceof Short)97return (Short) x;98return primitiveConversion(Wrapper.SHORT, x, cast).shortValue();99}100101static boolean unboxBoolean(Boolean x) {102return x;103}104static boolean unboxBoolean(Object x, boolean cast) {105if (x instanceof Boolean)106return (Boolean) x;107return (primitiveConversion(Wrapper.BOOLEAN, x, cast).intValue() & 1) != 0;108}109110static char unboxCharacter(Character x) {111return x;112}113static char unboxCharacter(Object x, boolean cast) {114if (x instanceof Character)115return (Character) x;116return (char) primitiveConversion(Wrapper.CHAR, x, cast).intValue();117}118119static long unboxLong(Long x) {120return x;121}122static long unboxLong(Object x, boolean cast) {123if (x instanceof Long)124return (Long) x;125return primitiveConversion(Wrapper.LONG, x, cast).longValue();126}127128static float unboxFloat(Float x) {129return x;130}131static float unboxFloat(Object x, boolean cast) {132if (x instanceof Float)133return (Float) x;134return primitiveConversion(Wrapper.FLOAT, x, cast).floatValue();135}136137static double unboxDouble(Double x) {138return x;139}140static double unboxDouble(Object x, boolean cast) {141if (x instanceof Double)142return (Double) x;143return primitiveConversion(Wrapper.DOUBLE, x, cast).doubleValue();144}145146private static MethodType unboxType(Wrapper wrap, int kind) {147if (kind == 0)148return MethodType.methodType(wrap.primitiveType(), wrap.wrapperType());149return MethodType.methodType(wrap.primitiveType(), Object.class, boolean.class);150}151152private static final WrapperCache[] UNBOX_CONVERSIONS = newWrapperCaches(4);153154private static MethodHandle unbox(Wrapper wrap, int kind) {155// kind 0 -> strongly typed with NPE156// kind 1 -> strongly typed but zero for null,157// kind 2 -> asType rules: accept multiple box types but only widening conversions with NPE158// kind 3 -> explicitCastArguments rules: allow narrowing conversions, zero for null159WrapperCache cache = UNBOX_CONVERSIONS[kind];160MethodHandle mh = cache.get(wrap);161if (mh != null) {162return mh;163}164// slow path165switch (wrap) {166case OBJECT:167case VOID:168throw new IllegalArgumentException("unbox "+wrap);169}170// look up the method171String name = "unbox" + wrap.wrapperSimpleName();172MethodType type = unboxType(wrap, kind);173try {174mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);175} catch (ReflectiveOperationException ex) {176mh = null;177}178if (mh != null) {179if (kind > 0) {180boolean cast = (kind != 2);181mh = MethodHandles.insertArguments(mh, 1, cast);182}183if (kind == 1) { // casting but exact (null -> zero)184mh = mh.asType(unboxType(wrap, 0));185}186return cache.put(wrap, mh);187}188throw new IllegalArgumentException("cannot find unbox adapter for " + wrap189+ (kind <= 1 ? " (exact)" : kind == 3 ? " (cast)" : ""));190}191192/** Return an exact unboxer for the given primitive type. */193public static MethodHandle unboxExact(Wrapper type) {194return unbox(type, 0);195}196197/** Return an exact unboxer for the given primitive type, with optional null-to-zero conversion.198* The boolean says whether to throw an NPE on a null value (false means unbox a zero).199* The type of the unboxer is of a form like (Integer)int.200*/201public static MethodHandle unboxExact(Wrapper type, boolean throwNPE) {202return unbox(type, throwNPE ? 0 : 1);203}204205/** Return a widening unboxer for the given primitive type.206* Widen narrower primitive boxes to the given type.207* Do not narrow any primitive values or convert null to zero.208* The type of the unboxer is of a form like (Object)int.209*/210public static MethodHandle unboxWiden(Wrapper type) {211return unbox(type, 2);212}213214/** Return a casting unboxer for the given primitive type.215* Widen or narrow primitive values to the given type, or convert null to zero, as needed.216* The type of the unboxer is of a form like (Object)int.217*/218public static MethodHandle unboxCast(Wrapper type) {219return unbox(type, 3);220}221222private static final Integer ZERO_INT = 0, ONE_INT = 1;223224/// Primitive conversions225/**226* Produce a Number which represents the given value {@code x}227* according to the primitive type of the given wrapper {@code wrap}.228* Caller must invoke intValue, byteValue, longValue (etc.) on the result229* to retrieve the desired primitive value.230*/231public static Number primitiveConversion(Wrapper wrap, Object x, boolean cast) {232// Maybe merge this code with Wrapper.convert/cast.233Number res;234if (x == null) {235if (!cast) return null;236return ZERO_INT;237}238if (x instanceof Number) {239res = (Number) x;240} else if (x instanceof Boolean) {241res = ((boolean)x ? ONE_INT : ZERO_INT);242} else if (x instanceof Character) {243res = (int)(char)x;244} else {245// this will fail with the required ClassCastException:246res = (Number) x;247}248Wrapper xwrap = Wrapper.findWrapperType(x.getClass());249if (xwrap == null || !cast && !wrap.isConvertibleFrom(xwrap))250// this will fail with the required ClassCastException:251return (Number) wrap.wrapperType().cast(x);252return res;253}254255/**256* The JVM verifier allows boolean, byte, short, or char to widen to int.257* Support exactly this conversion, from a boxed value type Boolean,258* Byte, Short, Character, or Integer.259*/260public static int widenSubword(Object x) {261if (x instanceof Integer)262return (int) x;263else if (x instanceof Boolean)264return fromBoolean((boolean) x);265else if (x instanceof Character)266return (char) x;267else if (x instanceof Short)268return (short) x;269else if (x instanceof Byte)270return (byte) x;271else272// Fail with a ClassCastException.273return (int) x;274}275276/// Converting primitives to references277278static Integer boxInteger(int x) {279return x;280}281282static Byte boxByte(byte x) {283return x;284}285286static Short boxShort(short x) {287return x;288}289290static Boolean boxBoolean(boolean x) {291return x;292}293294static Character boxCharacter(char x) {295return x;296}297298static Long boxLong(long x) {299return x;300}301302static Float boxFloat(float x) {303return x;304}305306static Double boxDouble(double x) {307return x;308}309310private static MethodType boxType(Wrapper wrap) {311// be exact, since return casts are hard to compose312Class<?> boxType = wrap.wrapperType();313return MethodType.methodType(boxType, wrap.primitiveType());314}315316private static final WrapperCache[] BOX_CONVERSIONS = newWrapperCaches(1);317318public static MethodHandle boxExact(Wrapper wrap) {319WrapperCache cache = BOX_CONVERSIONS[0];320MethodHandle mh = cache.get(wrap);321if (mh != null) {322return mh;323}324// look up the method325String name = "box" + wrap.wrapperSimpleName();326MethodType type = boxType(wrap);327try {328mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);329} catch (ReflectiveOperationException ex) {330mh = null;331}332if (mh != null) {333return cache.put(wrap, mh);334}335throw new IllegalArgumentException("cannot find box adapter for " + wrap);336}337338/// Constant functions339340static void ignore(Object x) {341// no value to return; this is an unbox of null342}343344static void empty() {345}346347static Object zeroObject() {348return null;349}350351static int zeroInteger() {352return 0;353}354355static long zeroLong() {356return 0;357}358359static float zeroFloat() {360return 0;361}362363static double zeroDouble() {364return 0;365}366367private static final WrapperCache[] CONSTANT_FUNCTIONS = newWrapperCaches(2);368369public static MethodHandle zeroConstantFunction(Wrapper wrap) {370WrapperCache cache = CONSTANT_FUNCTIONS[0];371MethodHandle mh = cache.get(wrap);372if (mh != null) {373return mh;374}375// slow path376MethodType type = MethodType.methodType(wrap.primitiveType());377switch (wrap) {378case VOID:379mh = Handles.EMPTY;380break;381case OBJECT:382case INT: case LONG: case FLOAT: case DOUBLE:383try {384mh = IMPL_LOOKUP.findStatic(THIS_CLASS, "zero"+wrap.wrapperSimpleName(), type);385} catch (ReflectiveOperationException ex) {386mh = null;387}388break;389}390if (mh != null) {391return cache.put(wrap, mh);392}393394// use zeroInt and cast the result395if (wrap.isSubwordOrInt() && wrap != Wrapper.INT) {396mh = MethodHandles.explicitCastArguments(zeroConstantFunction(Wrapper.INT), type);397return cache.put(wrap, mh);398}399throw new IllegalArgumentException("cannot find zero constant for " + wrap);400}401402private static class Handles {403static final MethodHandle CAST_REFERENCE, IGNORE, EMPTY;404static {405try {406MethodType idType = MethodType.genericMethodType(1);407MethodType ignoreType = idType.changeReturnType(void.class);408CAST_REFERENCE = IMPL_LOOKUP.findVirtual(Class.class, "cast", idType);409IGNORE = IMPL_LOOKUP.findStatic(THIS_CLASS, "ignore", ignoreType);410EMPTY = IMPL_LOOKUP.findStatic(THIS_CLASS, "empty", ignoreType.dropParameterTypes(0, 1));411} catch (NoSuchMethodException | IllegalAccessException ex) {412throw newInternalError("uncaught exception", ex);413}414}415}416417public static MethodHandle ignore() {418return Handles.IGNORE;419}420421/** Return a method that casts its second argument (an Object) to the given type (a Class). */422public static MethodHandle cast() {423return Handles.CAST_REFERENCE;424}425426/// Primitive conversions.427// These are supported directly by the JVM, usually by a single instruction.428// In the case of narrowing to a subword, there may be a pair of instructions.429// In the case of booleans, there may be a helper routine to manage a 1-bit value.430// This is the full 8x8 matrix (minus the diagonal).431432// narrow double to all other types:433static float doubleToFloat(double x) { // bytecode: d2f434return (float) x;435}436static long doubleToLong(double x) { // bytecode: d2l437return (long) x;438}439static int doubleToInt(double x) { // bytecode: d2i440return (int) x;441}442static short doubleToShort(double x) { // bytecodes: d2i, i2s443return (short) x;444}445static char doubleToChar(double x) { // bytecodes: d2i, i2c446return (char) x;447}448static byte doubleToByte(double x) { // bytecodes: d2i, i2b449return (byte) x;450}451static boolean doubleToBoolean(double x) {452return toBoolean((byte) x);453}454455// widen float:456static double floatToDouble(float x) { // bytecode: f2d457return x;458}459// narrow float:460static long floatToLong(float x) { // bytecode: f2l461return (long) x;462}463static int floatToInt(float x) { // bytecode: f2i464return (int) x;465}466static short floatToShort(float x) { // bytecodes: f2i, i2s467return (short) x;468}469static char floatToChar(float x) { // bytecodes: f2i, i2c470return (char) x;471}472static byte floatToByte(float x) { // bytecodes: f2i, i2b473return (byte) x;474}475static boolean floatToBoolean(float x) {476return toBoolean((byte) x);477}478479// widen long:480static double longToDouble(long x) { // bytecode: l2d481return x;482}483static float longToFloat(long x) { // bytecode: l2f484return x;485}486// narrow long:487static int longToInt(long x) { // bytecode: l2i488return (int) x;489}490static short longToShort(long x) { // bytecodes: f2i, i2s491return (short) x;492}493static char longToChar(long x) { // bytecodes: f2i, i2c494return (char) x;495}496static byte longToByte(long x) { // bytecodes: f2i, i2b497return (byte) x;498}499static boolean longToBoolean(long x) {500return toBoolean((byte) x);501}502503// widen int:504static double intToDouble(int x) { // bytecode: i2d505return x;506}507static float intToFloat(int x) { // bytecode: i2f508return x;509}510static long intToLong(int x) { // bytecode: i2l511return x;512}513// narrow int:514static short intToShort(int x) { // bytecode: i2s515return (short) x;516}517static char intToChar(int x) { // bytecode: i2c518return (char) x;519}520static byte intToByte(int x) { // bytecode: i2b521return (byte) x;522}523static boolean intToBoolean(int x) {524return toBoolean((byte) x);525}526527// widen short:528static double shortToDouble(short x) { // bytecode: i2d (implicit 's2i')529return x;530}531static float shortToFloat(short x) { // bytecode: i2f (implicit 's2i')532return x;533}534static long shortToLong(short x) { // bytecode: i2l (implicit 's2i')535return x;536}537static int shortToInt(short x) { // (implicit 's2i')538return x;539}540// narrow short:541static char shortToChar(short x) { // bytecode: i2c (implicit 's2i')542return (char)x;543}544static byte shortToByte(short x) { // bytecode: i2b (implicit 's2i')545return (byte)x;546}547static boolean shortToBoolean(short x) {548return toBoolean((byte) x);549}550551// widen char:552static double charToDouble(char x) { // bytecode: i2d (implicit 'c2i')553return x;554}555static float charToFloat(char x) { // bytecode: i2f (implicit 'c2i')556return x;557}558static long charToLong(char x) { // bytecode: i2l (implicit 'c2i')559return x;560}561static int charToInt(char x) { // (implicit 'c2i')562return x;563}564// narrow char:565static short charToShort(char x) { // bytecode: i2s (implicit 'c2i')566return (short)x;567}568static byte charToByte(char x) { // bytecode: i2b (implicit 'c2i')569return (byte)x;570}571static boolean charToBoolean(char x) {572return toBoolean((byte) x);573}574575// widen byte:576static double byteToDouble(byte x) { // bytecode: i2d (implicit 'b2i')577return x;578}579static float byteToFloat(byte x) { // bytecode: i2f (implicit 'b2i')580return x;581}582static long byteToLong(byte x) { // bytecode: i2l (implicit 'b2i')583return x;584}585static int byteToInt(byte x) { // (implicit 'b2i')586return x;587}588static short byteToShort(byte x) { // bytecode: i2s (implicit 'b2i')589return (short)x;590}591static char byteToChar(byte x) { // bytecode: i2b (implicit 'b2i')592return (char)x;593}594// narrow byte to boolean:595static boolean byteToBoolean(byte x) {596return toBoolean(x);597}598599// widen boolean to all types:600static double booleanToDouble(boolean x) {601return fromBoolean(x);602}603static float booleanToFloat(boolean x) {604return fromBoolean(x);605}606static long booleanToLong(boolean x) {607return fromBoolean(x);608}609static int booleanToInt(boolean x) {610return fromBoolean(x);611}612static short booleanToShort(boolean x) {613return fromBoolean(x);614}615static char booleanToChar(boolean x) {616return (char)fromBoolean(x);617}618static byte booleanToByte(boolean x) {619return fromBoolean(x);620}621622// helpers to force boolean into the conversion scheme:623static boolean toBoolean(byte x) {624// see javadoc for MethodHandles.explicitCastArguments625return ((x & 1) != 0);626}627static byte fromBoolean(boolean x) {628// see javadoc for MethodHandles.explicitCastArguments629return (x ? (byte)1 : (byte)0);630}631632private static final WrapperCache[] CONVERT_PRIMITIVE_FUNCTIONS = newWrapperCaches(Wrapper.COUNT);633634public static MethodHandle convertPrimitive(Wrapper wsrc, Wrapper wdst) {635WrapperCache cache = CONVERT_PRIMITIVE_FUNCTIONS[wsrc.ordinal()];636MethodHandle mh = cache.get(wdst);637if (mh != null) {638return mh;639}640// slow path641Class<?> src = wsrc.primitiveType();642Class<?> dst = wdst.primitiveType();643MethodType type = MethodType.methodType(dst, src);644if (wsrc == wdst) {645mh = MethodHandles.identity(src);646} else {647assert(src.isPrimitive() && dst.isPrimitive());648try {649mh = IMPL_LOOKUP.findStatic(THIS_CLASS, src.getSimpleName()+"To"+capitalize(dst.getSimpleName()), type);650} catch (ReflectiveOperationException ex) {651mh = null;652}653}654if (mh != null) {655assert(mh.type() == type) : mh;656return cache.put(wdst, mh);657}658659throw new IllegalArgumentException("cannot find primitive conversion function for " +660src.getSimpleName()+" -> "+dst.getSimpleName());661}662663public static MethodHandle convertPrimitive(Class<?> src, Class<?> dst) {664return convertPrimitive(Wrapper.forPrimitiveType(src), Wrapper.forPrimitiveType(dst));665}666667private static String capitalize(String x) {668return Character.toUpperCase(x.charAt(0))+x.substring(1);669}670671// handy shared exception makers (they simplify the common case code)672private static InternalError newInternalError(String message, Throwable cause) {673return new InternalError(message, cause);674}675private static InternalError newInternalError(Throwable cause) {676return new InternalError(cause);677}678}679680681