Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/invoke/util/ValueConversions.java
38918 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 java.util.EnumMap;3233public class ValueConversions {34private static final Class<?> THIS_CLASS = ValueConversions.class;35private static final Lookup IMPL_LOOKUP = MethodHandles.lookup();3637/** Thread-safe canonicalized mapping from Wrapper to MethodHandle38* with unsynchronized reads and synchronized writes.39* It's safe to publish MethodHandles by data race because they are immutable. */40private static class WrapperCache {41/** EnumMap uses preconstructed array internally, which is constant during it's lifetime. */42private final EnumMap<Wrapper, MethodHandle> map = new EnumMap<>(Wrapper.class);4344public MethodHandle get(Wrapper w) {45return map.get(w);46}47public synchronized MethodHandle put(final Wrapper w, final MethodHandle mh) {48// Simulate CAS to avoid racy duplication49MethodHandle prev = map.putIfAbsent(w, mh);50if (prev != null) return prev;51return mh;52}53}5455private static WrapperCache[] newWrapperCaches(int n) {56WrapperCache[] caches = new WrapperCache[n];57for (int i = 0; i < n; i++)58caches[i] = new WrapperCache();59return caches;60}6162/// Converting references to values.6364// There are several levels of this unboxing conversions:65// no conversions: exactly Integer.valueOf, etc.66// implicit conversions sanctioned by JLS 5.1.2, etc.67// explicit conversions as allowed by explicitCastArguments6869static int unboxInteger(Integer x) {70return x;71}72static int unboxInteger(Object x, boolean cast) {73if (x instanceof Integer)74return (Integer) x;75return primitiveConversion(Wrapper.INT, x, cast).intValue();76}7778static byte unboxByte(Byte x) {79return x;80}81static byte unboxByte(Object x, boolean cast) {82if (x instanceof Byte)83return (Byte) x;84return primitiveConversion(Wrapper.BYTE, x, cast).byteValue();85}8687static short unboxShort(Short x) {88return x;89}90static short unboxShort(Object x, boolean cast) {91if (x instanceof Short)92return (Short) x;93return primitiveConversion(Wrapper.SHORT, x, cast).shortValue();94}9596static boolean unboxBoolean(Boolean x) {97return x;98}99static boolean unboxBoolean(Object x, boolean cast) {100if (x instanceof Boolean)101return (Boolean) x;102return (primitiveConversion(Wrapper.BOOLEAN, x, cast).intValue() & 1) != 0;103}104105static char unboxCharacter(Character x) {106return x;107}108static char unboxCharacter(Object x, boolean cast) {109if (x instanceof Character)110return (Character) x;111return (char) primitiveConversion(Wrapper.CHAR, x, cast).intValue();112}113114static long unboxLong(Long x) {115return x;116}117static long unboxLong(Object x, boolean cast) {118if (x instanceof Long)119return (Long) x;120return primitiveConversion(Wrapper.LONG, x, cast).longValue();121}122123static float unboxFloat(Float x) {124return x;125}126static float unboxFloat(Object x, boolean cast) {127if (x instanceof Float)128return (Float) x;129return primitiveConversion(Wrapper.FLOAT, x, cast).floatValue();130}131132static double unboxDouble(Double x) {133return x;134}135static double unboxDouble(Object x, boolean cast) {136if (x instanceof Double)137return (Double) x;138return primitiveConversion(Wrapper.DOUBLE, x, cast).doubleValue();139}140141private static MethodType unboxType(Wrapper wrap, int kind) {142if (kind == 0)143return MethodType.methodType(wrap.primitiveType(), wrap.wrapperType());144return MethodType.methodType(wrap.primitiveType(), Object.class, boolean.class);145}146147private static final WrapperCache[] UNBOX_CONVERSIONS = newWrapperCaches(4);148149private static MethodHandle unbox(Wrapper wrap, int kind) {150// kind 0 -> strongly typed with NPE151// kind 1 -> strongly typed but zero for null,152// kind 2 -> asType rules: accept multiple box types but only widening conversions with NPE153// kind 3 -> explicitCastArguments rules: allow narrowing conversions, zero for null154WrapperCache cache = UNBOX_CONVERSIONS[kind];155MethodHandle mh = cache.get(wrap);156if (mh != null) {157return mh;158}159// slow path160switch (wrap) {161case OBJECT:162case VOID:163throw new IllegalArgumentException("unbox "+wrap);164}165// look up the method166String name = "unbox" + wrap.wrapperSimpleName();167MethodType type = unboxType(wrap, kind);168try {169mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);170} catch (ReflectiveOperationException ex) {171mh = null;172}173if (mh != null) {174if (kind > 0) {175boolean cast = (kind != 2);176mh = MethodHandles.insertArguments(mh, 1, cast);177}178if (kind == 1) { // casting but exact (null -> zero)179mh = mh.asType(unboxType(wrap, 0));180}181return cache.put(wrap, mh);182}183throw new IllegalArgumentException("cannot find unbox adapter for " + wrap184+ (kind <= 1 ? " (exact)" : kind == 3 ? " (cast)" : ""));185}186187/** Return an exact unboxer for the given primitive type. */188public static MethodHandle unboxExact(Wrapper type) {189return unbox(type, 0);190}191192/** Return an exact unboxer for the given primitive type, with optional null-to-zero conversion.193* The boolean says whether to throw an NPE on a null value (false means unbox a zero).194* The type of the unboxer is of a form like (Integer)int.195*/196public static MethodHandle unboxExact(Wrapper type, boolean throwNPE) {197return unbox(type, throwNPE ? 0 : 1);198}199200/** Return a widening unboxer for the given primitive type.201* Widen narrower primitive boxes to the given type.202* Do not narrow any primitive values or convert null to zero.203* The type of the unboxer is of a form like (Object)int.204*/205public static MethodHandle unboxWiden(Wrapper type) {206return unbox(type, 2);207}208209/** Return a casting unboxer for the given primitive type.210* Widen or narrow primitive values to the given type, or convert null to zero, as needed.211* The type of the unboxer is of a form like (Object)int.212*/213public static MethodHandle unboxCast(Wrapper type) {214return unbox(type, 3);215}216217static private final Integer ZERO_INT = 0, ONE_INT = 1;218219/// Primitive conversions220/**221* Produce a Number which represents the given value {@code x}222* according to the primitive type of the given wrapper {@code wrap}.223* Caller must invoke intValue, byteValue, longValue (etc.) on the result224* to retrieve the desired primitive value.225*/226public static Number primitiveConversion(Wrapper wrap, Object x, boolean cast) {227// Maybe merge this code with Wrapper.convert/cast.228Number res;229if (x == null) {230if (!cast) return null;231return ZERO_INT;232}233if (x instanceof Number) {234res = (Number) x;235} else if (x instanceof Boolean) {236res = ((boolean)x ? ONE_INT : ZERO_INT);237} else if (x instanceof Character) {238res = (int)(char)x;239} else {240// this will fail with the required ClassCastException:241res = (Number) x;242}243Wrapper xwrap = Wrapper.findWrapperType(x.getClass());244if (xwrap == null || !cast && !wrap.isConvertibleFrom(xwrap))245// this will fail with the required ClassCastException:246return (Number) wrap.wrapperType().cast(x);247return res;248}249250/**251* The JVM verifier allows boolean, byte, short, or char to widen to int.252* Support exactly this conversion, from a boxed value type Boolean,253* Byte, Short, Character, or Integer.254*/255public static int widenSubword(Object x) {256if (x instanceof Integer)257return (int) x;258else if (x instanceof Boolean)259return fromBoolean((boolean) x);260else if (x instanceof Character)261return (char) x;262else if (x instanceof Short)263return (short) x;264else if (x instanceof Byte)265return (byte) x;266else267// Fail with a ClassCastException.268return (int) x;269}270271/// Converting primitives to references272273static Integer boxInteger(int x) {274return x;275}276277static Byte boxByte(byte x) {278return x;279}280281static Short boxShort(short x) {282return x;283}284285static Boolean boxBoolean(boolean x) {286return x;287}288289static Character boxCharacter(char x) {290return x;291}292293static Long boxLong(long x) {294return x;295}296297static Float boxFloat(float x) {298return x;299}300301static Double boxDouble(double x) {302return x;303}304305private static MethodType boxType(Wrapper wrap) {306// be exact, since return casts are hard to compose307Class<?> boxType = wrap.wrapperType();308return MethodType.methodType(boxType, wrap.primitiveType());309}310311private static final WrapperCache[] BOX_CONVERSIONS = newWrapperCaches(1);312313public static MethodHandle boxExact(Wrapper wrap) {314WrapperCache cache = BOX_CONVERSIONS[0];315MethodHandle mh = cache.get(wrap);316if (mh != null) {317return mh;318}319// look up the method320String name = "box" + wrap.wrapperSimpleName();321MethodType type = boxType(wrap);322try {323mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);324} catch (ReflectiveOperationException ex) {325mh = null;326}327if (mh != null) {328return cache.put(wrap, mh);329}330throw new IllegalArgumentException("cannot find box adapter for " + wrap);331}332333/// Constant functions334335static void ignore(Object x) {336// no value to return; this is an unbox of null337}338339static void empty() {340}341342static Object zeroObject() {343return null;344}345346static int zeroInteger() {347return 0;348}349350static long zeroLong() {351return 0;352}353354static float zeroFloat() {355return 0;356}357358static double zeroDouble() {359return 0;360}361362private static final WrapperCache[] CONSTANT_FUNCTIONS = newWrapperCaches(2);363364public static MethodHandle zeroConstantFunction(Wrapper wrap) {365WrapperCache cache = CONSTANT_FUNCTIONS[0];366MethodHandle mh = cache.get(wrap);367if (mh != null) {368return mh;369}370// slow path371MethodType type = MethodType.methodType(wrap.primitiveType());372switch (wrap) {373case VOID:374mh = EMPTY;375break;376case OBJECT:377case INT: case LONG: case FLOAT: case DOUBLE:378try {379mh = IMPL_LOOKUP.findStatic(THIS_CLASS, "zero"+wrap.wrapperSimpleName(), type);380} catch (ReflectiveOperationException ex) {381mh = null;382}383break;384}385if (mh != null) {386return cache.put(wrap, mh);387}388389// use zeroInt and cast the result390if (wrap.isSubwordOrInt() && wrap != Wrapper.INT) {391mh = MethodHandles.explicitCastArguments(zeroConstantFunction(Wrapper.INT), type);392return cache.put(wrap, mh);393}394throw new IllegalArgumentException("cannot find zero constant for " + wrap);395}396397private static final MethodHandle CAST_REFERENCE, IGNORE, EMPTY;398static {399try {400MethodType idType = MethodType.genericMethodType(1);401MethodType ignoreType = idType.changeReturnType(void.class);402CAST_REFERENCE = IMPL_LOOKUP.findVirtual(Class.class, "cast", idType);403IGNORE = IMPL_LOOKUP.findStatic(THIS_CLASS, "ignore", ignoreType);404EMPTY = IMPL_LOOKUP.findStatic(THIS_CLASS, "empty", ignoreType.dropParameterTypes(0, 1));405} catch (NoSuchMethodException | IllegalAccessException ex) {406throw newInternalError("uncaught exception", ex);407}408}409410public static MethodHandle ignore() {411return IGNORE;412}413414/** Return a method that casts its second argument (an Object) to the given type (a Class). */415public static MethodHandle cast() {416return CAST_REFERENCE;417}418419/// Primitive conversions.420// These are supported directly by the JVM, usually by a single instruction.421// In the case of narrowing to a subword, there may be a pair of instructions.422// In the case of booleans, there may be a helper routine to manage a 1-bit value.423// This is the full 8x8 matrix (minus the diagonal).424425// narrow double to all other types:426static float doubleToFloat(double x) { // bytecode: d2f427return (float) x;428}429static long doubleToLong(double x) { // bytecode: d2l430return (long) x;431}432static int doubleToInt(double x) { // bytecode: d2i433return (int) x;434}435static short doubleToShort(double x) { // bytecodes: d2i, i2s436return (short) x;437}438static char doubleToChar(double x) { // bytecodes: d2i, i2c439return (char) x;440}441static byte doubleToByte(double x) { // bytecodes: d2i, i2b442return (byte) x;443}444static boolean doubleToBoolean(double x) {445return toBoolean((byte) x);446}447448// widen float:449static double floatToDouble(float x) { // bytecode: f2d450return x;451}452// narrow float:453static long floatToLong(float x) { // bytecode: f2l454return (long) x;455}456static int floatToInt(float x) { // bytecode: f2i457return (int) x;458}459static short floatToShort(float x) { // bytecodes: f2i, i2s460return (short) x;461}462static char floatToChar(float x) { // bytecodes: f2i, i2c463return (char) x;464}465static byte floatToByte(float x) { // bytecodes: f2i, i2b466return (byte) x;467}468static boolean floatToBoolean(float x) {469return toBoolean((byte) x);470}471472// widen long:473static double longToDouble(long x) { // bytecode: l2d474return x;475}476static float longToFloat(long x) { // bytecode: l2f477return x;478}479// narrow long:480static int longToInt(long x) { // bytecode: l2i481return (int) x;482}483static short longToShort(long x) { // bytecodes: f2i, i2s484return (short) x;485}486static char longToChar(long x) { // bytecodes: f2i, i2c487return (char) x;488}489static byte longToByte(long x) { // bytecodes: f2i, i2b490return (byte) x;491}492static boolean longToBoolean(long x) {493return toBoolean((byte) x);494}495496// widen int:497static double intToDouble(int x) { // bytecode: i2d498return x;499}500static float intToFloat(int x) { // bytecode: i2f501return x;502}503static long intToLong(int x) { // bytecode: i2l504return x;505}506// narrow int:507static short intToShort(int x) { // bytecode: i2s508return (short) x;509}510static char intToChar(int x) { // bytecode: i2c511return (char) x;512}513static byte intToByte(int x) { // bytecode: i2b514return (byte) x;515}516static boolean intToBoolean(int x) {517return toBoolean((byte) x);518}519520// widen short:521static double shortToDouble(short x) { // bytecode: i2d (implicit 's2i')522return x;523}524static float shortToFloat(short x) { // bytecode: i2f (implicit 's2i')525return x;526}527static long shortToLong(short x) { // bytecode: i2l (implicit 's2i')528return x;529}530static int shortToInt(short x) { // (implicit 's2i')531return x;532}533// narrow short:534static char shortToChar(short x) { // bytecode: i2c (implicit 's2i')535return (char)x;536}537static byte shortToByte(short x) { // bytecode: i2b (implicit 's2i')538return (byte)x;539}540static boolean shortToBoolean(short x) {541return toBoolean((byte) x);542}543544// widen char:545static double charToDouble(char x) { // bytecode: i2d (implicit 'c2i')546return x;547}548static float charToFloat(char x) { // bytecode: i2f (implicit 'c2i')549return x;550}551static long charToLong(char x) { // bytecode: i2l (implicit 'c2i')552return x;553}554static int charToInt(char x) { // (implicit 'c2i')555return x;556}557// narrow char:558static short charToShort(char x) { // bytecode: i2s (implicit 'c2i')559return (short)x;560}561static byte charToByte(char x) { // bytecode: i2b (implicit 'c2i')562return (byte)x;563}564static boolean charToBoolean(char x) {565return toBoolean((byte) x);566}567568// widen byte:569static double byteToDouble(byte x) { // bytecode: i2d (implicit 'b2i')570return x;571}572static float byteToFloat(byte x) { // bytecode: i2f (implicit 'b2i')573return x;574}575static long byteToLong(byte x) { // bytecode: i2l (implicit 'b2i')576return x;577}578static int byteToInt(byte x) { // (implicit 'b2i')579return x;580}581static short byteToShort(byte x) { // bytecode: i2s (implicit 'b2i')582return (short)x;583}584static char byteToChar(byte x) { // bytecode: i2b (implicit 'b2i')585return (char)x;586}587// narrow byte to boolean:588static boolean byteToBoolean(byte x) {589return toBoolean(x);590}591592// widen boolean to all types:593static double booleanToDouble(boolean x) {594return fromBoolean(x);595}596static float booleanToFloat(boolean x) {597return fromBoolean(x);598}599static long booleanToLong(boolean x) {600return fromBoolean(x);601}602static int booleanToInt(boolean x) {603return fromBoolean(x);604}605static short booleanToShort(boolean x) {606return fromBoolean(x);607}608static char booleanToChar(boolean x) {609return (char)fromBoolean(x);610}611static byte booleanToByte(boolean x) {612return fromBoolean(x);613}614615// helpers to force boolean into the conversion scheme:616static boolean toBoolean(byte x) {617// see javadoc for MethodHandles.explicitCastArguments618return ((x & 1) != 0);619}620static byte fromBoolean(boolean x) {621// see javadoc for MethodHandles.explicitCastArguments622return (x ? (byte)1 : (byte)0);623}624625private static final WrapperCache[] CONVERT_PRIMITIVE_FUNCTIONS = newWrapperCaches(Wrapper.values().length);626627public static MethodHandle convertPrimitive(Wrapper wsrc, Wrapper wdst) {628WrapperCache cache = CONVERT_PRIMITIVE_FUNCTIONS[wsrc.ordinal()];629MethodHandle mh = cache.get(wdst);630if (mh != null) {631return mh;632}633// slow path634Class<?> src = wsrc.primitiveType();635Class<?> dst = wdst.primitiveType();636MethodType type = MethodType.methodType(dst, src);637if (wsrc == wdst) {638mh = MethodHandles.identity(src);639} else {640assert(src.isPrimitive() && dst.isPrimitive());641try {642mh = IMPL_LOOKUP.findStatic(THIS_CLASS, src.getSimpleName()+"To"+capitalize(dst.getSimpleName()), type);643} catch (ReflectiveOperationException ex) {644mh = null;645}646}647if (mh != null) {648assert(mh.type() == type) : mh;649return cache.put(wdst, mh);650}651652throw new IllegalArgumentException("cannot find primitive conversion function for " +653src.getSimpleName()+" -> "+dst.getSimpleName());654}655656public static MethodHandle convertPrimitive(Class<?> src, Class<?> dst) {657return convertPrimitive(Wrapper.forPrimitiveType(src), Wrapper.forPrimitiveType(dst));658}659660private static String capitalize(String x) {661return Character.toUpperCase(x.charAt(0))+x.substring(1);662}663664// handy shared exception makers (they simplify the common case code)665private static InternalError newInternalError(String message, Throwable cause) {666return new InternalError(message, cause);667}668private static InternalError newInternalError(Throwable cause) {669return new InternalError(cause);670}671}672673674