ValueConversions.java 21.7 KB
Newer Older
1
/*
2
 * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
3 4 5 6
 * 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
7
 * published by the Free Software Foundation.  Oracle designates this
8
 * particular file as subject to the "Classpath" exception as provided
9
 * by Oracle in the LICENSE file that accompanied this code.
10 11 12 13 14 15 16 17 18 19 20
 *
 * 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.
 *
21 22 23
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
24 25
 */

26
package sun.invoke.util;
27

28 29 30 31
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
32 33 34
import java.util.EnumMap;

public class ValueConversions {
35
    private static final Class<?> THIS_CLASS = ValueConversions.class;
36
    private static final Lookup IMPL_LOOKUP = MethodHandles.lookup();
37

38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
    /** Thread-safe canonicalized mapping from Wrapper to MethodHandle
     * with unsynchronized reads and synchronized writes.
     * It's safe to publish MethodHandles by data race because they are immutable. */
    private static class WrapperCache {
        /** EnumMap uses preconstructed array internally, which is constant during it's lifetime. */
        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) {
            // Simulate CAS to avoid racy duplication
            MethodHandle prev = map.putIfAbsent(w, mh);
            if (prev != null)  return prev;
            return mh;
        }
    }

    private static WrapperCache[] newWrapperCaches(int n) {
        WrapperCache[] caches = new WrapperCache[n];
58
        for (int i = 0; i < n; i++)
59
            caches[i] = new WrapperCache();
60 61 62 63 64
        return caches;
    }

    /// Converting references to values.

65 66 67 68 69
    // 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

70 71 72
    static int unboxInteger(Integer x) {
        return x;
    }
73 74
    static int unboxInteger(Object x, boolean cast) {
        if (x instanceof Integer)
75
            return (Integer) x;
76
        return primitiveConversion(Wrapper.INT, x, cast).intValue();
77 78
    }

79 80 81
    static byte unboxByte(Byte x) {
        return x;
    }
82 83
    static byte unboxByte(Object x, boolean cast) {
        if (x instanceof Byte)
84
            return (Byte) x;
85
        return primitiveConversion(Wrapper.BYTE, x, cast).byteValue();
86 87
    }

88 89 90
    static short unboxShort(Short x) {
        return x;
    }
91 92
    static short unboxShort(Object x, boolean cast) {
        if (x instanceof Short)
93
            return (Short) x;
94
        return primitiveConversion(Wrapper.SHORT, x, cast).shortValue();
95 96
    }

97 98 99
    static boolean unboxBoolean(Boolean x) {
        return x;
    }
100 101
    static boolean unboxBoolean(Object x, boolean cast) {
        if (x instanceof Boolean)
102
            return (Boolean) x;
103
        return (primitiveConversion(Wrapper.BOOLEAN, x, cast).intValue() & 1) != 0;
104 105
    }

106 107 108
    static char unboxCharacter(Character x) {
        return x;
    }
109 110
    static char unboxCharacter(Object x, boolean cast) {
        if (x instanceof Character)
111
            return (Character) x;
112
        return (char) primitiveConversion(Wrapper.CHAR, x, cast).intValue();
113 114
    }

115 116 117
    static long unboxLong(Long x) {
        return x;
    }
118 119
    static long unboxLong(Object x, boolean cast) {
        if (x instanceof Long)
120
            return (Long) x;
121
        return primitiveConversion(Wrapper.LONG, x, cast).longValue();
122 123
    }

124 125 126
    static float unboxFloat(Float x) {
        return x;
    }
127 128
    static float unboxFloat(Object x, boolean cast) {
        if (x instanceof Float)
129
            return (Float) x;
130
        return primitiveConversion(Wrapper.FLOAT, x, cast).floatValue();
131 132
    }

133 134 135
    static double unboxDouble(Double x) {
        return x;
    }
136 137
    static double unboxDouble(Object x, boolean cast) {
        if (x instanceof Double)
138
            return (Double) x;
139
        return primitiveConversion(Wrapper.DOUBLE, x, cast).doubleValue();
140 141
    }

142 143 144
    private static MethodType unboxType(Wrapper wrap, int kind) {
        if (kind == 0)
            return MethodType.methodType(wrap.primitiveType(), wrap.wrapperType());
145
        return MethodType.methodType(wrap.primitiveType(), Object.class, boolean.class);
146 147
    }

148
    private static final WrapperCache[] UNBOX_CONVERSIONS = newWrapperCaches(4);
149

150 151 152 153 154 155
    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
        // kind 3 -> explicitCastArguments rules: allow narrowing conversions, zero for null
        WrapperCache cache = UNBOX_CONVERSIONS[kind];
156 157 158 159 160 161 162 163
        MethodHandle mh = cache.get(wrap);
        if (mh != null) {
            return mh;
        }
        // slow path
        switch (wrap) {
            case OBJECT:
            case VOID:
164
                throw new IllegalArgumentException("unbox "+wrap);
165 166
        }
        // look up the method
167
        String name = "unbox" + wrap.wrapperSimpleName();
168
        MethodType type = unboxType(wrap, kind);
169 170 171 172
        try {
            mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);
        } catch (ReflectiveOperationException ex) {
            mh = null;
173
        }
174
        if (mh != null) {
175 176 177 178 179 180 181 182
            if (kind > 0) {
                boolean cast = (kind != 2);
                mh = MethodHandles.insertArguments(mh, 1, cast);
            }
            if (kind == 1) {  // casting but exact (null -> zero)
                mh = mh.asType(unboxType(wrap, 0));
            }
            return cache.put(wrap, mh);
183
        }
184
        throw new IllegalArgumentException("cannot find unbox adapter for " + wrap
185
                + (kind <= 1 ? " (exact)" : kind == 3 ? " (cast)" : ""));
186 187
    }

188 189 190 191 192 193 194 195 196 197 198
    /** Return an exact unboxer for the given primitive type. */
    public static MethodHandle unboxExact(Wrapper type) {
        return unbox(type, 0);
    }

    /** Return an exact unboxer for the given primitive type, with optional null-to-zero conversion.
     *  The boolean says whether to throw an NPE on a null value (false means unbox a zero).
     *  The type of the unboxer is of a form like (Integer)int.
     */
    public static MethodHandle unboxExact(Wrapper type, boolean throwNPE) {
        return unbox(type, throwNPE ? 0 : 1);
199 200
    }

201 202 203 204 205 206 207
    /** Return a widening unboxer for the given primitive type.
     *  Widen narrower primitive boxes to the given type.
     *  Do not narrow any primitive values or convert null to zero.
     *  The type of the unboxer is of a form like (Object)int.
     */
    public static MethodHandle unboxWiden(Wrapper type) {
        return unbox(type, 2);
208 209
    }

210 211 212 213 214 215
    /** Return a casting unboxer for the given primitive type.
     *  Widen or narrow primitive values to the given type, or convert null to zero, as needed.
     *  The type of the unboxer is of a form like (Object)int.
     */
    public static MethodHandle unboxCast(Wrapper type) {
        return unbox(type, 3);
216 217
    }

218 219
    static private final Integer ZERO_INT = 0, ONE_INT = 1;

220
    /// Primitive conversions
221 222 223 224 225 226
    /**
     * Produce a Number which represents the given value {@code x}
     * according to the primitive type of the given wrapper {@code wrap}.
     * Caller must invoke intValue, byteValue, longValue (etc.) on the result
     * to retrieve the desired primitive value.
     */
227 228
    public static Number primitiveConversion(Wrapper wrap, Object x, boolean cast) {
        // Maybe merge this code with Wrapper.convert/cast.
229
        Number res;
230 231
        if (x == null) {
            if (!cast)  return null;
232
            return ZERO_INT;
233 234 235 236
        }
        if (x instanceof Number) {
            res = (Number) x;
        } else if (x instanceof Boolean) {
237
            res = ((boolean)x ? ONE_INT : ZERO_INT);
238 239 240 241 242 243
        } else if (x instanceof Character) {
            res = (int)(char)x;
        } else {
            // this will fail with the required ClassCastException:
            res = (Number) x;
        }
244 245
        Wrapper xwrap = Wrapper.findWrapperType(x.getClass());
        if (xwrap == null || !cast && !wrap.isConvertibleFrom(xwrap))
246
            // this will fail with the required ClassCastException:
247
            return (Number) wrap.wrapperType().cast(x);
248
        return res;
249 250
    }

251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271
    /**
     * The JVM verifier allows boolean, byte, short, or char to widen to int.
     * Support exactly this conversion, from a boxed value type Boolean,
     * Byte, Short, Character, or Integer.
     */
    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
            // Fail with a ClassCastException.
            return (int) x;
    }

272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
    /// 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;
    }

306
    private static MethodType boxType(Wrapper wrap) {
307 308
        // be exact, since return casts are hard to compose
        Class<?> boxType = wrap.wrapperType();
309
        return MethodType.methodType(boxType, wrap.primitiveType());
310 311
    }

312
    private static final WrapperCache[] BOX_CONVERSIONS = newWrapperCaches(1);
313

314 315
    public static MethodHandle boxExact(Wrapper wrap) {
        WrapperCache cache = BOX_CONVERSIONS[0];
316 317 318 319 320
        MethodHandle mh = cache.get(wrap);
        if (mh != null) {
            return mh;
        }
        // look up the method
321 322
        String name = "box" + wrap.wrapperSimpleName();
        MethodType type = boxType(wrap);
323 324 325 326
        try {
            mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);
        } catch (ReflectiveOperationException ex) {
            mh = null;
327
        }
328
        if (mh != null) {
329
            return cache.put(wrap, mh);
330
        }
331
        throw new IllegalArgumentException("cannot find box adapter for " + wrap);
332 333
    }

334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362
    /// 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;
    }

363
    private static final WrapperCache[] CONSTANT_FUNCTIONS = newWrapperCaches(2);
364 365

    public static MethodHandle zeroConstantFunction(Wrapper wrap) {
366
        WrapperCache cache = CONSTANT_FUNCTIONS[0];
367 368 369 370 371
        MethodHandle mh = cache.get(wrap);
        if (mh != null) {
            return mh;
        }
        // slow path
372
        MethodType type = MethodType.methodType(wrap.primitiveType());
373 374 375 376
        switch (wrap) {
            case VOID:
                mh = EMPTY;
                break;
377
            case OBJECT:
378
            case INT: case LONG: case FLOAT: case DOUBLE:
379
                try {
380
                    mh = IMPL_LOOKUP.findStatic(THIS_CLASS, "zero"+wrap.wrapperSimpleName(), type);
381
                } catch (ReflectiveOperationException ex) {
382 383
                    mh = null;
                }
384 385 386
                break;
        }
        if (mh != null) {
387
            return cache.put(wrap, mh);
388 389
        }

390 391 392
        // use zeroInt and cast the result
        if (wrap.isSubwordOrInt() && wrap != Wrapper.INT) {
            mh = MethodHandles.explicitCastArguments(zeroConstantFunction(Wrapper.INT), type);
393
            return cache.put(wrap, mh);
394 395 396 397
        }
        throw new IllegalArgumentException("cannot find zero constant for " + wrap);
    }

398
    private static final MethodHandle CAST_REFERENCE, IGNORE, EMPTY;
399 400
    static {
        try {
401
            MethodType idType = MethodType.genericMethodType(1);
402
            MethodType ignoreType = idType.changeReturnType(void.class);
403
            CAST_REFERENCE = IMPL_LOOKUP.findVirtual(Class.class, "cast", idType);
404 405 406
            IGNORE = IMPL_LOOKUP.findStatic(THIS_CLASS, "ignore", ignoreType);
            EMPTY = IMPL_LOOKUP.findStatic(THIS_CLASS, "empty", ignoreType.dropParameterTypes(0, 1));
        } catch (NoSuchMethodException | IllegalAccessException ex) {
407
            throw newInternalError("uncaught exception", ex);
408 409 410
        }
    }

411 412
    public static MethodHandle ignore() {
        return IGNORE;
413 414
    }

415 416 417 418 419
    /** Return a method that casts its second argument (an Object) to the given type (a Class). */
    public static MethodHandle cast() {
        return CAST_REFERENCE;
    }

420 421 422 423 424
    /// 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).
425

426 427
    // narrow double to all other types:
    static float doubleToFloat(double x) {  // bytecode: d2f
428 429
        return (float) x;
    }
430
    static long doubleToLong(double x) {  // bytecode: d2l
431 432
        return (long) x;
    }
433
    static int doubleToInt(double x) {  // bytecode: d2i
434 435
        return (int) x;
    }
436
    static short doubleToShort(double x) {  // bytecodes: d2i, i2s
437 438
        return (short) x;
    }
439
    static char doubleToChar(double x) {  // bytecodes: d2i, i2c
440 441
        return (char) x;
    }
442
    static byte doubleToByte(double x) {  // bytecodes: d2i, i2b
443 444 445 446 447 448
        return (byte) x;
    }
    static boolean doubleToBoolean(double x) {
        return toBoolean((byte) x);
    }

449 450 451 452 453 454
    // widen float:
    static double floatToDouble(float x) {  // bytecode: f2d
        return x;
    }
    // narrow float:
    static long floatToLong(float x) {  // bytecode: f2l
455 456
        return (long) x;
    }
457
    static int floatToInt(float x) {  // bytecode: f2i
458 459
        return (int) x;
    }
460
    static short floatToShort(float x) {  // bytecodes: f2i, i2s
461 462
        return (short) x;
    }
463
    static char floatToChar(float x) {  // bytecodes: f2i, i2c
464 465
        return (char) x;
    }
466
    static byte floatToByte(float x) {  // bytecodes: f2i, i2b
467 468 469 470 471 472
        return (byte) x;
    }
    static boolean floatToBoolean(float x) {
        return toBoolean((byte) x);
    }

473 474
    // widen long:
    static double longToDouble(long x) {  // bytecode: l2d
475 476
        return x;
    }
477
    static float longToFloat(long x) {  // bytecode: l2f
478 479
        return x;
    }
480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498
    // narrow long:
    static int longToInt(long x) {  // bytecode: l2i
        return (int) x;
    }
    static short longToShort(long x) {  // bytecodes: f2i, i2s
        return (short) x;
    }
    static char longToChar(long x) {  // bytecodes: f2i, i2c
        return (char) x;
    }
    static byte longToByte(long x) {  // bytecodes: f2i, i2b
        return (byte) x;
    }
    static boolean longToBoolean(long x) {
        return toBoolean((byte) x);
    }

    // widen int:
    static double intToDouble(int x) {  // bytecode: i2d
499 500
        return x;
    }
501
    static float intToFloat(int x) {  // bytecode: i2f
502 503
        return x;
    }
504
    static long intToLong(int x) {  // bytecode: i2l
505 506
        return x;
    }
507 508 509 510 511 512 513 514 515 516 517 518
    // narrow int:
    static short intToShort(int x) {  // bytecode: i2s
        return (short) x;
    }
    static char intToChar(int x) {  // bytecode: i2c
        return (char) x;
    }
    static byte intToByte(int x) {  // bytecode: i2b
        return (byte) x;
    }
    static boolean intToBoolean(int x) {
        return toBoolean((byte) x);
519 520
    }

521 522
    // widen short:
    static double shortToDouble(short x) {  // bytecode: i2d (implicit 's2i')
523 524
        return x;
    }
525
    static float shortToFloat(short x) {  // bytecode: i2f (implicit 's2i')
526 527
        return x;
    }
528
    static long shortToLong(short x) {  // bytecode: i2l (implicit 's2i')
529 530
        return x;
    }
531
    static int shortToInt(short x) {  // (implicit 's2i')
532 533
        return x;
    }
534 535 536 537 538 539 540 541 542 543 544 545 546
    // narrow short:
    static char shortToChar(short x) {  // bytecode: i2c (implicit 's2i')
        return (char)x;
    }
    static byte shortToByte(short x) {  // bytecode: i2b (implicit 's2i')
        return (byte)x;
    }
    static boolean shortToBoolean(short x) {
        return toBoolean((byte) x);
    }

    // widen char:
    static double charToDouble(char x) {  // bytecode: i2d (implicit 'c2i')
547 548
        return x;
    }
549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596
    static float charToFloat(char x) {  // bytecode: i2f (implicit 'c2i')
        return x;
    }
    static long charToLong(char x) {  // bytecode: i2l (implicit 'c2i')
        return x;
    }
    static int charToInt(char x) {  // (implicit 'c2i')
        return x;
    }
    // narrow char:
    static short charToShort(char x) {  // bytecode: i2s (implicit 'c2i')
        return (short)x;
    }
    static byte charToByte(char x) {  // bytecode: i2b (implicit 'c2i')
        return (byte)x;
    }
    static boolean charToBoolean(char x) {
        return toBoolean((byte) x);
    }

    // widen byte:
    static double byteToDouble(byte x) {  // bytecode: i2d (implicit 'b2i')
        return x;
    }
    static float byteToFloat(byte x) {  // bytecode: i2f (implicit 'b2i')
        return x;
    }
    static long byteToLong(byte x) {  // bytecode: i2l (implicit 'b2i')
        return x;
    }
    static int byteToInt(byte x) {  // (implicit 'b2i')
        return x;
    }
    static short byteToShort(byte x) {  // bytecode: i2s (implicit 'b2i')
        return (short)x;
    }
    static char byteToChar(byte x) {  // bytecode: i2b (implicit 'b2i')
        return (char)x;
    }
    // narrow byte to boolean:
    static boolean byteToBoolean(byte x) {
        return toBoolean(x);
    }

    // widen boolean to all types:
    static double booleanToDouble(boolean x) {
        return fromBoolean(x);
    }
597 598 599
    static float booleanToFloat(boolean x) {
        return fromBoolean(x);
    }
600 601 602 603 604 605 606 607 608 609 610 611 612 613 614
    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);
    }
615

616
    // helpers to force boolean into the conversion scheme:
617 618 619 620 621 622 623 624 625
    static boolean toBoolean(byte x) {
        // see javadoc for MethodHandles.explicitCastArguments
        return ((x & 1) != 0);
    }
    static byte fromBoolean(boolean x) {
        // see javadoc for MethodHandles.explicitCastArguments
        return (x ? (byte)1 : (byte)0);
    }

626
    private static final WrapperCache[] CONVERT_PRIMITIVE_FUNCTIONS = newWrapperCaches(Wrapper.values().length);
627

628
    public static MethodHandle convertPrimitive(Wrapper wsrc, Wrapper wdst) {
629
        WrapperCache cache = CONVERT_PRIMITIVE_FUNCTIONS[wsrc.ordinal()];
630
        MethodHandle mh = cache.get(wdst);
631 632 633 634
        if (mh != null) {
            return mh;
        }
        // slow path
635 636
        Class<?> src = wsrc.primitiveType();
        Class<?> dst = wdst.primitiveType();
637
        MethodType type = MethodType.methodType(dst, src);
638
        if (wsrc == wdst) {
639
            mh = MethodHandles.identity(src);
640 641 642 643 644 645 646
        } 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;
            }
647 648 649
        }
        if (mh != null) {
            assert(mh.type() == type) : mh;
650
            return cache.put(wdst, mh);
651 652
        }

653
        throw new IllegalArgumentException("cannot find primitive conversion function for " +
654 655 656
                                           src.getSimpleName()+" -> "+dst.getSimpleName());
    }

657 658
    public static MethodHandle convertPrimitive(Class<?> src, Class<?> dst) {
        return convertPrimitive(Wrapper.forPrimitiveType(src), Wrapper.forPrimitiveType(dst));
659 660 661 662 663 664
    }

    private static String capitalize(String x) {
        return Character.toUpperCase(x.charAt(0))+x.substring(1);
    }

665 666 667 668 669 670 671
    // handy shared exception makers (they simplify the common case code)
    private static InternalError newInternalError(String message, Throwable cause) {
        return new InternalError(message, cause);
    }
    private static InternalError newInternalError(Throwable cause) {
        return new InternalError(cause);
    }
672
}