1 /*
   2  * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * 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  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.invoke.util;
  27 
  28 import java.lang.invoke.MethodHandle;
  29 import java.lang.invoke.MethodHandles;
  30 import java.lang.invoke.MethodHandles.Lookup;
  31 import java.lang.invoke.MethodType;
  32 import java.util.EnumMap;
  33 
  34 public class ValueConversions {
  35     private static final Class<?> THIS_CLASS = ValueConversions.class;
  36     private static final Lookup IMPL_LOOKUP = MethodHandles.lookup();
  37 
  38     /** Thread-safe canonicalized mapping from Wrapper to MethodHandle
  39      * with unsynchronized reads and synchronized writes.
  40      * It's safe to publish MethodHandles by data race because they are immutable. */
  41     private static class WrapperCache {
  42         /** EnumMap uses preconstructed array internally, which is constant during it's lifetime. */
  43         private final EnumMap<Wrapper, MethodHandle> map = new EnumMap<>(Wrapper.class);
  44 
  45         public MethodHandle get(Wrapper w) {
  46             return map.get(w);
  47         }
  48         public synchronized MethodHandle put(final Wrapper w, final MethodHandle mh) {
  49             // Simulate CAS to avoid racy duplication
  50             MethodHandle prev = map.putIfAbsent(w, mh);
  51             if (prev != null)  return prev;
  52             return mh;
  53         }
  54     }
  55 
  56     private static WrapperCache[] newWrapperCaches(int n) {
  57         WrapperCache[] caches = new WrapperCache[n];
  58         for (int i = 0; i < n; i++)
  59             caches[i] = new WrapperCache();
  60         return caches;
  61     }
  62 
  63     /// Converting references to values.
  64 
  65     // There are several levels of this unboxing conversions:
  66     //   no conversions:  exactly Integer.valueOf, etc.
  67     //   implicit conversions sanctioned by JLS 5.1.2, etc.
  68     //   explicit conversions as allowed by explicitCastArguments
  69 
  70     static int unboxInteger(Integer x) {
  71         return x;
  72     }
  73     static int unboxInteger(Object x, boolean cast) {
  74         if (x instanceof Integer)
  75             return (Integer) x;
  76         return primitiveConversion(Wrapper.INT, x, cast).intValue();
  77     }
  78 
  79     static byte unboxByte(Byte x) {
  80         return x;
  81     }
  82     static byte unboxByte(Object x, boolean cast) {
  83         if (x instanceof Byte)
  84             return (Byte) x;
  85         return primitiveConversion(Wrapper.BYTE, x, cast).byteValue();
  86     }
  87 
  88     static short unboxShort(Short x) {
  89         return x;
  90     }
  91     static short unboxShort(Object x, boolean cast) {
  92         if (x instanceof Short)
  93             return (Short) x;
  94         return primitiveConversion(Wrapper.SHORT, x, cast).shortValue();
  95     }
  96 
  97     static boolean unboxBoolean(Boolean x) {
  98         return x;
  99     }
 100     static boolean unboxBoolean(Object x, boolean cast) {
 101         if (x instanceof Boolean)
 102             return (Boolean) x;
 103         return (primitiveConversion(Wrapper.BOOLEAN, x, cast).intValue() & 1) != 0;
 104     }
 105 
 106     static char unboxCharacter(Character x) {
 107         return x;
 108     }
 109     static char unboxCharacter(Object x, boolean cast) {
 110         if (x instanceof Character)
 111             return (Character) x;
 112         return (char) primitiveConversion(Wrapper.CHAR, x, cast).intValue();
 113     }
 114 
 115     static long unboxLong(Long x) {
 116         return x;
 117     }
 118     static long unboxLong(Object x, boolean cast) {
 119         if (x instanceof Long)
 120             return (Long) x;
 121         return primitiveConversion(Wrapper.LONG, x, cast).longValue();
 122     }
 123 
 124     static float unboxFloat(Float x) {
 125         return x;
 126     }
 127     static float unboxFloat(Object x, boolean cast) {
 128         if (x instanceof Float)
 129             return (Float) x;
 130         return primitiveConversion(Wrapper.FLOAT, x, cast).floatValue();
 131     }
 132 
 133     static double unboxDouble(Double x) {
 134         return x;
 135     }
 136     static double unboxDouble(Object x, boolean cast) {
 137         if (x instanceof Double)
 138             return (Double) x;
 139         return primitiveConversion(Wrapper.DOUBLE, x, cast).doubleValue();
 140     }
 141 
 142     private static MethodType unboxType(Wrapper wrap, int kind) {
 143         if (kind == 0)
 144             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     private static MethodHandle unbox(Wrapper wrap, int kind) {
 151         // kind 0 -> strongly typed with NPE
 152         // kind 1 -> strongly typed but zero for null,
 153         // kind 2 -> asType rules: accept multiple box types but only widening conversions with NPE
 154         // kind 3 -> explicitCastArguments rules: allow narrowing conversions, zero for null
 155         WrapperCache cache = UNBOX_CONVERSIONS[kind];
 156         MethodHandle mh = cache.get(wrap);
 157         if (mh != null) {
 158             return mh;
 159         }
 160         // slow path
 161         switch (wrap) {
 162             case OBJECT:
 163             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         try {
 170             mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);
 171         } catch (ReflectiveOperationException ex) {
 172             mh = null;
 173         }
 174         if (mh != null) {
 175             if (kind > 0) {
 176                 boolean cast = (kind != 2);
 177                 mh = MethodHandles.insertArguments(mh, 1, cast);
 178             }
 179             if (kind == 1) {  // casting but exact (null -> zero)
 180                 mh = mh.asType(unboxType(wrap, 0));
 181             }
 182             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     /** Return an exact unboxer for the given primitive type. */
 189     public static MethodHandle unboxExact(Wrapper type) {
 190         return unbox(type, 0);
 191     }
 192 
 193     /** Return an exact unboxer for the given primitive type, with optional null-to-zero conversion.
 194      *  The boolean says whether to throw an NPE on a null value (false means unbox a zero).
 195      *  The type of the unboxer is of a form like (Integer)int.
 196      */
 197     public static MethodHandle unboxExact(Wrapper type, boolean throwNPE) {
 198         return unbox(type, throwNPE ? 0 : 1);
 199     }
 200 
 201     /** Return a widening unboxer for the given primitive type.
 202      *  Widen narrower primitive boxes to the given type.
 203      *  Do not narrow any primitive values or convert null to zero.
 204      *  The type of the unboxer is of a form like (Object)int.
 205      */
 206     public static MethodHandle unboxWiden(Wrapper type) {
 207         return unbox(type, 2);
 208     }
 209 
 210     /** Return a casting unboxer for the given primitive type.
 211      *  Widen or narrow primitive values to the given type, or convert null to zero, as needed.
 212      *  The type of the unboxer is of a form like (Object)int.
 213      */
 214     public static MethodHandle unboxCast(Wrapper type) {
 215         return unbox(type, 3);
 216     }
 217 
 218     static private final Integer ZERO_INT = 0, ONE_INT = 1;
 219 
 220     /// Primitive conversions
 221     /**
 222      * Produce a Number which represents the given value {@code x}
 223      * according to the primitive type of the given wrapper {@code wrap}.
 224      * Caller must invoke intValue, byteValue, longValue (etc.) on the result
 225      * to retrieve the desired primitive value.
 226      */
 227     public static Number primitiveConversion(Wrapper wrap, Object x, boolean cast) {
 228         // Maybe merge this code with Wrapper.convert/cast.
 229         Number res;
 230         if (x == null) {
 231             if (!cast)  return null;
 232             return ZERO_INT;
 233         }
 234         if (x instanceof Number) {
 235             res = (Number) x;
 236         } else if (x instanceof Boolean) {
 237             res = ((boolean)x ? ONE_INT : ZERO_INT);
 238         } else if (x instanceof Character) {
 239             res = (int)(char)x;
 240         } else {
 241             // this will fail with the required ClassCastException:
 242             res = (Number) x;
 243         }
 244         Wrapper xwrap = Wrapper.findWrapperType(x.getClass());
 245         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      * The JVM verifier allows boolean, byte, short, or char to widen to int.
 253      * Support exactly this conversion, from a boxed value type Boolean,
 254      * Byte, Short, Character, or Integer.
 255      */
 256     public static int widenSubword(Object x) {
 257         if (x instanceof Integer)
 258             return (int) x;
 259         else if (x instanceof Boolean)
 260             return fromBoolean((boolean) x);
 261         else if (x instanceof Character)
 262             return (char) x;
 263         else if (x instanceof Short)
 264             return (short) x;
 265         else if (x instanceof Byte)
 266             return (byte) x;
 267         else
 268             // Fail with a ClassCastException.
 269             return (int) x;
 270     }
 271 
 272     /// Converting primitives to references
 273 
 274     static Integer boxInteger(int x) {
 275         return x;
 276     }
 277 
 278     static Byte boxByte(byte x) {
 279         return x;
 280     }
 281 
 282     static Short boxShort(short x) {
 283         return x;
 284     }
 285 
 286     static Boolean boxBoolean(boolean x) {
 287         return x;
 288     }
 289 
 290     static Character boxCharacter(char x) {
 291         return x;
 292     }
 293 
 294     static Long boxLong(long x) {
 295         return x;
 296     }
 297 
 298     static Float boxFloat(float x) {
 299         return x;
 300     }
 301 
 302     static Double boxDouble(double x) {
 303         return x;
 304     }
 305 
 306     private static MethodType boxType(Wrapper wrap) {
 307         // be exact, since return casts are hard to compose
 308         Class<?> boxType = wrap.wrapperType();
 309         return MethodType.methodType(boxType, wrap.primitiveType());
 310     }
 311 
 312     private static final WrapperCache[] BOX_CONVERSIONS = newWrapperCaches(1);
 313 
 314     public static MethodHandle boxExact(Wrapper wrap) {
 315         WrapperCache cache = BOX_CONVERSIONS[0];
 316         MethodHandle mh = cache.get(wrap);
 317         if (mh != null) {
 318             return mh;
 319         }
 320         // look up the method
 321         String name = "box" + wrap.wrapperSimpleName();
 322         MethodType type = boxType(wrap);
 323         try {
 324             mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);
 325         } catch (ReflectiveOperationException ex) {
 326             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     /// Constant functions
 335 
 336     static void ignore(Object x) {
 337         // no value to return; this is an unbox of null
 338     }
 339 
 340     static void empty() {
 341     }
 342 
 343     static Object zeroObject() {
 344         return null;
 345     }
 346 
 347     static int zeroInteger() {
 348         return 0;
 349     }
 350 
 351     static long zeroLong() {
 352         return 0;
 353     }
 354 
 355     static float zeroFloat() {
 356         return 0;
 357     }
 358 
 359     static double zeroDouble() {
 360         return 0;
 361     }
 362 
 363     private static final WrapperCache[] CONSTANT_FUNCTIONS = newWrapperCaches(2);
 364 
 365     public static MethodHandle zeroConstantFunction(Wrapper wrap) {
 366         WrapperCache cache = CONSTANT_FUNCTIONS[0];
 367         MethodHandle mh = cache.get(wrap);
 368         if (mh != null) {
 369             return mh;
 370         }
 371         // slow path
 372         MethodType type = MethodType.methodType(wrap.primitiveType());
 373         switch (wrap) {
 374             case VOID:
 375                 mh = EMPTY;
 376                 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                     mh = null;
 383                 }
 384                 break;
 385         }
 386         if (mh != null) {
 387             return cache.put(wrap, mh);
 388         }
 389 
 390         // use zeroInt and cast the result
 391         if (wrap.isSubwordOrInt() && wrap != Wrapper.INT) {
 392             mh = MethodHandles.explicitCastArguments(zeroConstantFunction(Wrapper.INT), type);
 393             return cache.put(wrap, mh);
 394         }
 395         throw new IllegalArgumentException("cannot find zero constant for " + wrap);
 396     }
 397 
 398     /// Converting references to references.
 399 
 400     /**
 401      * Identity function.
 402      * @param x an arbitrary reference value
 403      * @return the same value x
 404      */
 405     static <T> T identity(T x) {
 406         return x;
 407     }
 408 
 409     static <T> T[] identity(T[] x) {
 410         return x;
 411     }
 412 
 413     /**
 414      * Identity function on ints.
 415      * @param x an arbitrary int value
 416      * @return the same value x
 417      */
 418     static int identity(int x) {
 419         return x;
 420     }
 421 
 422     static byte identity(byte x) {
 423         return x;
 424     }
 425 
 426     static short identity(short x) {
 427         return x;
 428     }
 429 
 430     static boolean identity(boolean x) {
 431         return x;
 432     }
 433 
 434     static char identity(char x) {
 435         return x;
 436     }
 437 
 438     /**
 439      * Identity function on longs.
 440      * @param x an arbitrary long value
 441      * @return the same value x
 442      */
 443     static long identity(long x) {
 444         return x;
 445     }
 446 
 447     static float identity(float x) {
 448         return x;
 449     }
 450 
 451     static double identity(double x) {
 452         return x;
 453     }
 454 
 455     private static final MethodHandle IDENTITY, CAST_REFERENCE, IGNORE, EMPTY;
 456     static {
 457         try {
 458             MethodType idType = MethodType.genericMethodType(1);
 459             MethodType ignoreType = idType.changeReturnType(void.class);
 460             IDENTITY = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", idType);
 461             CAST_REFERENCE = IMPL_LOOKUP.findVirtual(Class.class, "cast", idType);
 462             IGNORE = IMPL_LOOKUP.findStatic(THIS_CLASS, "ignore", ignoreType);
 463             EMPTY = IMPL_LOOKUP.findStatic(THIS_CLASS, "empty", ignoreType.dropParameterTypes(0, 1));
 464         } catch (NoSuchMethodException | IllegalAccessException ex) {
 465             throw newInternalError("uncaught exception", ex);
 466         }
 467     }
 468 
 469     public static MethodHandle ignore() {
 470         return IGNORE;
 471     }
 472 
 473     public static MethodHandle identity() {
 474         return IDENTITY;
 475     }
 476 
 477     public static MethodHandle identity(Class<?> type) {
 478         if (!type.isPrimitive())
 479             // Reference identity has been moved into MethodHandles:
 480             return MethodHandles.identity(type);
 481         return identity(Wrapper.findPrimitiveType(type));
 482     }
 483 
 484     public static MethodHandle identity(Wrapper wrap) {
 485         WrapperCache cache = CONSTANT_FUNCTIONS[1];
 486         MethodHandle mh = cache.get(wrap);
 487         if (mh != null) {
 488             return mh;
 489         }
 490         // slow path
 491         MethodType type = MethodType.methodType(wrap.primitiveType());
 492         if (wrap != Wrapper.VOID)
 493             type = type.appendParameterTypes(wrap.primitiveType());
 494         try {
 495             mh = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", type);
 496         } catch (ReflectiveOperationException ex) {
 497             mh = null;
 498         }
 499         if (mh == null && wrap == Wrapper.VOID) {
 500             mh = EMPTY;  // #(){} : #()void
 501         }
 502         if (mh != null) {
 503             return cache.put(wrap, mh);
 504         }
 505         throw new IllegalArgumentException("cannot find identity for " + wrap);
 506     }
 507 
 508     /** Return a method that casts its second argument (an Object) to the given type (a Class). */
 509     public static MethodHandle cast() {
 510         return CAST_REFERENCE;
 511     }
 512 
 513     /// Primitive conversions.
 514     // These are supported directly by the JVM, usually by a single instruction.
 515     // In the case of narrowing to a subword, there may be a pair of instructions.
 516     // In the case of booleans, there may be a helper routine to manage a 1-bit value.
 517     // This is the full 8x8 matrix (minus the diagonal).
 518 
 519     // narrow double to all other types:
 520     static float doubleToFloat(double x) {  // bytecode: d2f
 521         return (float) x;
 522     }
 523     static long doubleToLong(double x) {  // bytecode: d2l
 524         return (long) x;
 525     }
 526     static int doubleToInt(double x) {  // bytecode: d2i
 527         return (int) x;
 528     }
 529     static short doubleToShort(double x) {  // bytecodes: d2i, i2s
 530         return (short) x;
 531     }
 532     static char doubleToChar(double x) {  // bytecodes: d2i, i2c
 533         return (char) x;
 534     }
 535     static byte doubleToByte(double x) {  // bytecodes: d2i, i2b
 536         return (byte) x;
 537     }
 538     static boolean doubleToBoolean(double x) {
 539         return toBoolean((byte) x);
 540     }
 541 
 542     // widen float:
 543     static double floatToDouble(float x) {  // bytecode: f2d
 544         return x;
 545     }
 546     // narrow float:
 547     static long floatToLong(float x) {  // bytecode: f2l
 548         return (long) x;
 549     }
 550     static int floatToInt(float x) {  // bytecode: f2i
 551         return (int) x;
 552     }
 553     static short floatToShort(float x) {  // bytecodes: f2i, i2s
 554         return (short) x;
 555     }
 556     static char floatToChar(float x) {  // bytecodes: f2i, i2c
 557         return (char) x;
 558     }
 559     static byte floatToByte(float x) {  // bytecodes: f2i, i2b
 560         return (byte) x;
 561     }
 562     static boolean floatToBoolean(float x) {
 563         return toBoolean((byte) x);
 564     }
 565 
 566     // widen long:
 567     static double longToDouble(long x) {  // bytecode: l2d
 568         return x;
 569     }
 570     static float longToFloat(long x) {  // bytecode: l2f
 571         return x;
 572     }
 573     // narrow long:
 574     static int longToInt(long x) {  // bytecode: l2i
 575         return (int) x;
 576     }
 577     static short longToShort(long x) {  // bytecodes: f2i, i2s
 578         return (short) x;
 579     }
 580     static char longToChar(long x) {  // bytecodes: f2i, i2c
 581         return (char) x;
 582     }
 583     static byte longToByte(long x) {  // bytecodes: f2i, i2b
 584         return (byte) x;
 585     }
 586     static boolean longToBoolean(long x) {
 587         return toBoolean((byte) x);
 588     }
 589 
 590     // widen int:
 591     static double intToDouble(int x) {  // bytecode: i2d
 592         return x;
 593     }
 594     static float intToFloat(int x) {  // bytecode: i2f
 595         return x;
 596     }
 597     static long intToLong(int x) {  // bytecode: i2l
 598         return x;
 599     }
 600     // narrow int:
 601     static short intToShort(int x) {  // bytecode: i2s
 602         return (short) x;
 603     }
 604     static char intToChar(int x) {  // bytecode: i2c
 605         return (char) x;
 606     }
 607     static byte intToByte(int x) {  // bytecode: i2b
 608         return (byte) x;
 609     }
 610     static boolean intToBoolean(int x) {
 611         return toBoolean((byte) x);
 612     }
 613 
 614     // widen short:
 615     static double shortToDouble(short x) {  // bytecode: i2d (implicit 's2i')
 616         return x;
 617     }
 618     static float shortToFloat(short x) {  // bytecode: i2f (implicit 's2i')
 619         return x;
 620     }
 621     static long shortToLong(short x) {  // bytecode: i2l (implicit 's2i')
 622         return x;
 623     }
 624     static int shortToInt(short x) {  // (implicit 's2i')
 625         return x;
 626     }
 627     // narrow short:
 628     static char shortToChar(short x) {  // bytecode: i2c (implicit 's2i')
 629         return (char)x;
 630     }
 631     static byte shortToByte(short x) {  // bytecode: i2b (implicit 's2i')
 632         return (byte)x;
 633     }
 634     static boolean shortToBoolean(short x) {
 635         return toBoolean((byte) x);
 636     }
 637 
 638     // widen char:
 639     static double charToDouble(char x) {  // bytecode: i2d (implicit 'c2i')
 640         return x;
 641     }
 642     static float charToFloat(char x) {  // bytecode: i2f (implicit 'c2i')
 643         return x;
 644     }
 645     static long charToLong(char x) {  // bytecode: i2l (implicit 'c2i')
 646         return x;
 647     }
 648     static int charToInt(char x) {  // (implicit 'c2i')
 649         return x;
 650     }
 651     // narrow char:
 652     static short charToShort(char x) {  // bytecode: i2s (implicit 'c2i')
 653         return (short)x;
 654     }
 655     static byte charToByte(char x) {  // bytecode: i2b (implicit 'c2i')
 656         return (byte)x;
 657     }
 658     static boolean charToBoolean(char x) {
 659         return toBoolean((byte) x);
 660     }
 661 
 662     // widen byte:
 663     static double byteToDouble(byte x) {  // bytecode: i2d (implicit 'b2i')
 664         return x;
 665     }
 666     static float byteToFloat(byte x) {  // bytecode: i2f (implicit 'b2i')
 667         return x;
 668     }
 669     static long byteToLong(byte x) {  // bytecode: i2l (implicit 'b2i')
 670         return x;
 671     }
 672     static int byteToInt(byte x) {  // (implicit 'b2i')
 673         return x;
 674     }
 675     static short byteToShort(byte x) {  // bytecode: i2s (implicit 'b2i')
 676         return (short)x;
 677     }
 678     static char byteToChar(byte x) {  // bytecode: i2b (implicit 'b2i')
 679         return (char)x;
 680     }
 681     // narrow byte to boolean:
 682     static boolean byteToBoolean(byte x) {
 683         return toBoolean(x);
 684     }
 685 
 686     // widen boolean to all types:
 687     static double booleanToDouble(boolean x) {
 688         return fromBoolean(x);
 689     }
 690     static float booleanToFloat(boolean x) {
 691         return fromBoolean(x);
 692     }
 693     static long booleanToLong(boolean x) {
 694         return fromBoolean(x);
 695     }
 696     static int booleanToInt(boolean x) {
 697         return fromBoolean(x);
 698     }
 699     static short booleanToShort(boolean x) {
 700         return fromBoolean(x);
 701     }
 702     static char booleanToChar(boolean x) {
 703         return (char)fromBoolean(x);
 704     }
 705     static byte booleanToByte(boolean x) {
 706         return fromBoolean(x);
 707     }
 708 
 709     // helpers to force boolean into the conversion scheme:
 710     static boolean toBoolean(byte x) {
 711         // see javadoc for MethodHandles.explicitCastArguments
 712         return ((x & 1) != 0);
 713     }
 714     static byte fromBoolean(boolean x) {
 715         // see javadoc for MethodHandles.explicitCastArguments
 716         return (x ? (byte)1 : (byte)0);
 717     }
 718 
 719     private static final WrapperCache[] CONVERT_PRIMITIVE_FUNCTIONS = newWrapperCaches(Wrapper.values().length);
 720 
 721     public static MethodHandle convertPrimitive(Wrapper wsrc, Wrapper wdst) {
 722         WrapperCache cache = CONVERT_PRIMITIVE_FUNCTIONS[wsrc.ordinal()];
 723         MethodHandle mh = cache.get(wdst);
 724         if (mh != null) {
 725             return mh;
 726         }
 727         // slow path
 728         Class<?> src = wsrc.primitiveType();
 729         Class<?> dst = wdst.primitiveType();
 730         MethodType type = MethodType.methodType(dst, src);
 731         if (wsrc == wdst) {
 732             mh = MethodHandles.identity(src);
 733         } else {
 734             assert(src.isPrimitive() && dst.isPrimitive());
 735             try {
 736                 mh = IMPL_LOOKUP.findStatic(THIS_CLASS, src.getSimpleName()+"To"+capitalize(dst.getSimpleName()), type);
 737             } catch (ReflectiveOperationException ex) {
 738                 mh = null;
 739             }
 740         }
 741         if (mh != null) {
 742             assert(mh.type() == type) : mh;
 743             return cache.put(wdst, mh);
 744         }
 745 
 746         throw new IllegalArgumentException("cannot find primitive conversion function for " +
 747                                            src.getSimpleName()+" -> "+dst.getSimpleName());
 748     }
 749 
 750     public static MethodHandle convertPrimitive(Class<?> src, Class<?> dst) {
 751         return convertPrimitive(Wrapper.forPrimitiveType(src), Wrapper.forPrimitiveType(dst));
 752     }
 753 
 754     private static String capitalize(String x) {
 755         return Character.toUpperCase(x.charAt(0))+x.substring(1);
 756     }
 757 
 758     // handy shared exception makers (they simplify the common case code)
 759     private static InternalError newInternalError(String message, Throwable cause) {
 760         return new InternalError(message, cause);
 761     }
 762     private static InternalError newInternalError(Throwable cause) {
 763         return new InternalError(cause);
 764     }
 765 }