Showing the changes to java/pkg/sun/invoke/util/Wrapper.java (1 of 18352 files). Show all
Unified · Split
| @@ -0,0 +1,661 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (c) 2008, 2011, 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 | +public enum Wrapper { | |
| 29 | + BOOLEAN(Boolean.class, boolean.class, 'Z', (Boolean)false, new boolean[0], Format.unsigned(1)), | |
| 30 | + // These must be in the order defined for widening primitive conversions in JLS 5.1.2 | |
| 31 | + BYTE(Byte.class, byte.class, 'B', (Byte)(byte)0, new byte[0], Format.signed(8)), | |
| 32 | + SHORT(Short.class, short.class, 'S', (Short)(short)0, new short[0], Format.signed(16)), | |
| 33 | + CHAR(Character.class, char.class, 'C', (Character)(char)0, new char[0], Format.unsigned(16)), | |
| 34 | + INT(Integer.class, int.class, 'I', (Integer)(int)0, new int[0], Format.signed(32)), | |
| 35 | + LONG(Long.class, long.class, 'J', (Long)(long)0, new long[0], Format.signed(64)), | |
| 36 | + FLOAT(Float.class, float.class, 'F', (Float)(float)0, new float[0], Format.floating(32)), | |
| 37 | + DOUBLE(Double.class, double.class, 'D', (Double)(double)0, new double[0], Format.floating(64)), | |
| 38 | + //NULL(Null.class, null.class, 'N', null, null, Format.other(1)), | |
| 39 | + OBJECT(Object.class, Object.class, 'L', null, new Object[0], Format.other(1)), | |
| 40 | + // VOID must be the last type, since it is "assignable" from any other type: | |
| 41 | + VOID(Void.class, void.class, 'V', null, null, Format.other(0)), | |
| 42 | + ; | |
| 43 | + | |
| 44 | + private final Class<?> wrapperType; | |
| 45 | + private final Class<?> primitiveType; | |
| 46 | + private final char basicTypeChar; | |
| 47 | + private final Object zero; | |
| 48 | + private final Object emptyArray; | |
| 49 | + private final int format; | |
| 50 | + private final String simpleName; | |
| 51 | + | |
| 52 | + private Wrapper(Class<?> wtype, Class<?> ptype, char tchar, Object zero, Object emptyArray, int format) { | |
| 53 | + this.wrapperType = wtype; | |
| 54 | + this.primitiveType = ptype; | |
| 55 | + this.basicTypeChar = tchar; | |
| 56 | + this.zero = zero; | |
| 57 | + this.emptyArray = emptyArray; | |
| 58 | + this.format = format; | |
| 59 | + this.simpleName = wtype.getSimpleName(); | |
| 60 | + } | |
| 61 | + | |
| 62 | + /** For debugging, give the details of this wrapper. */ | |
| 63 | + public String detailString() { | |
| 64 | + return simpleName+ | |
| 65 | + java.util.Arrays.asList(wrapperType, primitiveType, | |
| 66 | + basicTypeChar, zero, | |
| 67 | + "0x"+Integer.toHexString(format)); | |
| 68 | + } | |
| 69 | + | |
| 70 | + private static abstract class Format { | |
| 71 | + static final int SLOT_SHIFT = 0, SIZE_SHIFT = 2, KIND_SHIFT = 12; | |
| 72 | + static final int | |
| 73 | + SIGNED = (-1) << KIND_SHIFT, | |
| 74 | + UNSIGNED = 0 << KIND_SHIFT, | |
| 75 | + FLOATING = 1 << KIND_SHIFT; | |
| 76 | + static final int | |
| 77 | + SLOT_MASK = ((1<<(SIZE_SHIFT-SLOT_SHIFT))-1), | |
| 78 | + SIZE_MASK = ((1<<(KIND_SHIFT-SIZE_SHIFT))-1); | |
| 79 | + static int format(int kind, int size, int slots) { | |
| 80 | + assert(((kind >> KIND_SHIFT) << KIND_SHIFT) == kind); | |
| 81 | + assert((size & (size-1)) == 0); // power of two | |
| 82 | + assert((kind == SIGNED) ? (size > 0) : | |
| 83 | + (kind == UNSIGNED) ? (size > 0) : | |
| 84 | + (kind == FLOATING) ? (size == 32 || size == 64) : | |
| 85 | + false); | |
| 86 | + assert((slots == 2) ? (size == 64) : | |
| 87 | + (slots == 1) ? (size <= 32) : | |
| 88 | + false); | |
| 89 | + return kind | (size << SIZE_SHIFT) | (slots << SLOT_SHIFT); | |
| 90 | + } | |
| 91 | + static final int | |
| 92 | + INT = SIGNED | (32 << SIZE_SHIFT) | (1 << SLOT_SHIFT), | |
| 93 | + SHORT = SIGNED | (16 << SIZE_SHIFT) | (1 << SLOT_SHIFT), | |
| 94 | + BOOLEAN = UNSIGNED | (1 << SIZE_SHIFT) | (1 << SLOT_SHIFT), | |
| 95 | + CHAR = UNSIGNED | (16 << SIZE_SHIFT) | (1 << SLOT_SHIFT), | |
| 96 | + FLOAT = FLOATING | (32 << SIZE_SHIFT) | (1 << SLOT_SHIFT), | |
| 97 | + VOID = UNSIGNED | (0 << SIZE_SHIFT) | (0 << SLOT_SHIFT), | |
| 98 | + NUM_MASK = (-1) << SIZE_SHIFT; | |
| 99 | + static int signed(int size) { return format(SIGNED, size, (size > 32 ? 2 : 1)); } | |
| 100 | + static int unsigned(int size) { return format(UNSIGNED, size, (size > 32 ? 2 : 1)); } | |
| 101 | + static int floating(int size) { return format(FLOATING, size, (size > 32 ? 2 : 1)); } | |
| 102 | + static int other(int slots) { return slots << SLOT_SHIFT; } | |
| 103 | + } | |
| 104 | + | |
| 105 | + /// format queries: | |
| 106 | + | |
| 107 | + /** How many bits are in the wrapped value? Returns 0 for OBJECT or VOID. */ | |
| 108 | + public int bitWidth() { return (format >> Format.SIZE_SHIFT) & Format.SIZE_MASK; } | |
| 109 | + /** How many JVM stack slots occupied by the wrapped value? Returns 0 for VOID. */ | |
| 110 | + public int stackSlots() { return (format >> Format.SLOT_SHIFT) & Format.SLOT_MASK; } | |
| 111 | + /** Does the wrapped value occupy a single JVM stack slot? */ | |
| 112 | + public boolean isSingleWord() { return (format & (1 << Format.SLOT_SHIFT)) != 0; } | |
| 113 | + /** Does the wrapped value occupy two JVM stack slots? */ | |
| 114 | + public boolean isDoubleWord() { return (format & (2 << Format.SLOT_SHIFT)) != 0; } | |
| 115 | + /** Is the wrapped type numeric (not void or object)? */ | |
| 116 | + public boolean isNumeric() { return (format & Format.NUM_MASK) != 0; } | |
| 117 | + /** Is the wrapped type a primitive other than float, double, or void? */ | |
| 118 | + public boolean isIntegral() { return isNumeric() && format < Format.FLOAT; } | |
| 119 | + /** Is the wrapped type one of int, boolean, byte, char, or short? */ | |
| 120 | + public boolean isSubwordOrInt() { return isIntegral() && isSingleWord(); } | |
| 121 | + /* Is the wrapped value a signed integral type (one of byte, short, int, or long)? */ | |
| 122 | + public boolean isSigned() { return format < Format.VOID; } | |
| 123 | + /* Is the wrapped value an unsigned integral type (one of boolean or char)? */ | |
| 124 | + public boolean isUnsigned() { return format >= Format.BOOLEAN && format < Format.FLOAT; } | |
| 125 | + /** Is the wrapped type either float or double? */ | |
| 126 | + public boolean isFloating() { return format >= Format.FLOAT; } | |
| 127 | + /** Is the wrapped type either void or a reference? */ | |
| 128 | + public boolean isOther() { return (format & ~Format.SLOT_MASK) == 0; } | |
| 129 | + | |
| 130 | + /** Does the JLS 5.1.2 allow a variable of this wrapper's | |
| 131 | + * primitive type to be assigned from a value of the given wrapper's primitive type? | |
| 132 | + * Cases: | |
| 133 | + * <ul> | |
| 134 | + * <li>unboxing followed by widening primitive conversion | |
| 135 | + * <li>any type converted to {@code void} (i.e., dropping a method call's value) | |
| 136 | + * <li>boxing conversion followed by widening reference conversion to {@code Object} | |
| 137 | + * </ul> | |
| 138 | + * These are the cases allowed by MethodHandle.asType. | |
| 139 | + */ | |
| 140 | + public boolean isConvertibleFrom(Wrapper source) { | |
| 141 | + if (this == source) return true; | |
| 142 | + if (this.compareTo(source) < 0) { | |
| 143 | + // At best, this is a narrowing conversion. | |
| 144 | + return false; | |
| 145 | + } | |
| 146 | + // All conversions are allowed in the enum order between floats and signed ints. | |
| 147 | + // First detect non-signed non-float types (boolean, char, Object, void). | |
| 148 | + boolean floatOrSigned = (((this.format & source.format) & Format.SIGNED) != 0); | |
| 149 | + if (!floatOrSigned) { | |
| 150 | + if (this.isOther()) return true; | |
| 151 | + // can convert char to int or wider, but nothing else | |
| 152 | + if (source.format == Format.CHAR) return true; | |
| 153 | + // no other conversions are classified as widening | |
| 154 | + return false; | |
| 155 | + } | |
| 156 | + // All signed and float conversions in the enum order are widening. | |
| 157 | + assert(this.isFloating() || this.isSigned()); | |
| 158 | + assert(source.isFloating() || source.isSigned()); | |
| 159 | + return true; | |
| 160 | + } | |
| 161 | + | |
| 162 | + static { assert(checkConvertibleFrom()); } | |
| 163 | + private static boolean checkConvertibleFrom() { | |
| 164 | + // Check the matrix for correct classification of widening conversions. | |
| 165 | + for (Wrapper w : values()) { | |
| 166 | + assert(w.isConvertibleFrom(w)); | |
| 167 | + assert(VOID.isConvertibleFrom(w)); | |
| 168 | + if (w != VOID) { | |
| 169 | + assert(OBJECT.isConvertibleFrom(w)); | |
| 170 | + assert(!w.isConvertibleFrom(VOID)); | |
| 171 | + } | |
| 172 | + // check relations with unsigned integral types: | |
| 173 | + if (w != CHAR) { | |
| 174 | + assert(!CHAR.isConvertibleFrom(w)); | |
| 175 | + if (!w.isConvertibleFrom(INT)) | |
| 176 | + assert(!w.isConvertibleFrom(CHAR)); | |
| 177 | + } | |
| 178 | + if (w != BOOLEAN) { | |
| 179 | + assert(!BOOLEAN.isConvertibleFrom(w)); | |
| 180 | + if (w != VOID && w != OBJECT) | |
| 181 | + assert(!w.isConvertibleFrom(BOOLEAN)); | |
| 182 | + } | |
| 183 | + // check relations with signed integral types: | |
| 184 | + if (w.isSigned()) { | |
| 185 | + for (Wrapper x : values()) { | |
| 186 | + if (w == x) continue; | |
| 187 | + if (x.isFloating()) | |
| 188 | + assert(!w.isConvertibleFrom(x)); | |
| 189 | + else if (x.isSigned()) { | |
| 190 | + if (w.compareTo(x) < 0) | |
| 191 | + assert(!w.isConvertibleFrom(x)); | |
| 192 | + else | |
| 193 | + assert(w.isConvertibleFrom(x)); | |
| 194 | + } | |
| 195 | + } | |
| 196 | + } | |
| 197 | + // check relations with floating types: | |
| 198 | + if (w.isFloating()) { | |
| 199 | + for (Wrapper x : values()) { | |
| 200 | + if (w == x) continue; | |
| 201 | + if (x.isSigned()) | |
| 202 | + assert(w.isConvertibleFrom(x)); | |
| 203 | + else if (x.isFloating()) { | |
| 204 | + if (w.compareTo(x) < 0) | |
| 205 | + assert(!w.isConvertibleFrom(x)); | |
| 206 | + else | |
| 207 | + assert(w.isConvertibleFrom(x)); | |
| 208 | + } | |
| 209 | + } | |
| 210 | + } | |
| 211 | + } | |
| 212 | + return true; // i.e., assert(true) | |
| 213 | + } | |
| 214 | + | |
| 215 | + /** Produce a zero value for the given wrapper type. | |
| 216 | + * This will be a numeric zero for a number or character, | |
| 217 | + * false for a boolean, and null for a reference or void. | |
| 218 | + * The common thread is that this is what is contained | |
| 219 | + * in a default-initialized variable of the given primitive | |
| 220 | + * type. (For void, it is what a reflective method returns | |
| 221 | + * instead of no value at all.) | |
| 222 | + */ | |
| 223 | + public Object zero() { return zero; } | |
| 224 | + | |
| 225 | + /** Produce a zero value for the given wrapper type T. | |
| 226 | + * The optional argument must a type compatible with this wrapper. | |
| 227 | + * Equivalent to {@code this.cast(this.zero(), type)}. | |
| 228 | + */ | |
| 229 | + public <T> T zero(Class<T> type) { return convert(zero, type); } | |
| 230 | + | |
| 231 | +// /** Produce a wrapper for the given wrapper or primitive type. */ | |
| 232 | +// public static Wrapper valueOf(Class<?> type) { | |
| 233 | +// if (isPrimitiveType(type)) | |
| 234 | +// return forPrimitiveType(type); | |
| 235 | +// else | |
| 236 | +// return forWrapperType(type); | |
| 237 | +// } | |
| 238 | + | |
| 239 | + /** Return the wrapper that wraps values of the given type. | |
| 240 | + * The type may be {@code Object}, meaning the {@code OBJECT} wrapper. | |
| 241 | + * Otherwise, the type must be a primitive. | |
| 242 | + * @throws IllegalArgumentException for unexpected types | |
| 243 | + */ | |
| 244 | + public static Wrapper forPrimitiveType(Class<?> type) { | |
| 245 | + Wrapper w = findPrimitiveType(type); | |
| 246 | + if (w != null) return w; | |
| 247 | + if (type.isPrimitive()) | |
| 248 | + throw new InternalError(); // redo hash function | |
| 249 | + throw newIllegalArgumentException("not primitive: "+type); | |
| 250 | + } | |
| 251 | + | |
| 252 | + static Wrapper findPrimitiveType(Class<?> type) { | |
| 253 | + Wrapper w = FROM_PRIM[hashPrim(type)]; | |
| 254 | + if (w != null && w.primitiveType == type) { | |
| 255 | + return w; | |
| 256 | + } | |
| 257 | + return null; | |
| 258 | + } | |
| 259 | + | |
| 260 | + /** Return the wrapper that wraps values into the given wrapper type. | |
| 261 | + * If it is {@code Object}, return {@code OBJECT}. | |
| 262 | + * Otherwise, it must be a wrapper type. | |
| 263 | + * The type must not be a primitive type. | |
| 264 | + * @throws IllegalArgumentException for unexpected types | |
| 265 | + */ | |
| 266 | + public static Wrapper forWrapperType(Class<?> type) { | |
| 267 | + Wrapper w = findWrapperType(type); | |
| 268 | + if (w != null) return w; | |
| 269 | + for (Wrapper x : values()) | |
| 270 | + if (x.wrapperType == type) | |
| 271 | + throw new InternalError(); // redo hash function | |
| 272 | + throw newIllegalArgumentException("not wrapper: "+type); | |
| 273 | + } | |
| 274 | + | |
| 275 | + static Wrapper findWrapperType(Class<?> type) { | |
| 276 | + Wrapper w = FROM_WRAP[hashWrap(type)]; | |
| 277 | + if (w != null && w.wrapperType == type) { | |
| 278 | + return w; | |
| 279 | + } | |
| 280 | + return null; | |
| 281 | + } | |
| 282 | + | |
| 283 | + /** Return the wrapper that corresponds to the given bytecode | |
| 284 | + * signature character. Return {@code OBJECT} for the character 'L'. | |
| 285 | + * @throws IllegalArgumentException for any non-signature character or {@code '['}. | |
| 286 | + */ | |
| 287 | + public static Wrapper forBasicType(char type) { | |
| 288 | + Wrapper w = FROM_CHAR[hashChar(type)]; | |
| 289 | + if (w != null && w.basicTypeChar == type) { | |
| 290 | + return w; | |
| 291 | + } | |
| 292 | + for (Wrapper x : values()) | |
| 293 | + if (w.basicTypeChar == type) | |
| 294 | + throw new InternalError(); // redo hash function | |
| 295 | + throw newIllegalArgumentException("not basic type char: "+type); | |
| 296 | + } | |
| 297 | + | |
| 298 | + /** Return the wrapper for the given type, if it is | |
| 299 | + * a primitive type, else return {@code OBJECT}. | |
| 300 | + */ | |
| 301 | + public static Wrapper forBasicType(Class<?> type) { | |
| 302 | + if (type.isPrimitive()) | |
| 303 | + return forPrimitiveType(type); | |
| 304 | + return OBJECT; // any reference, including wrappers or arrays | |
| 305 | + } | |
| 306 | + | |
| 307 | + // Note on perfect hashes: | |
| 308 | + // for signature chars c, do (c + (c >> 1)) % 16 | |
| 309 | + // for primitive type names n, do (n[0] + n[2]) % 16 | |
| 310 | + // The type name hash works for both primitive and wrapper names. | |
| 311 | + // You can add "java/lang/Object" to the primitive names. | |
| 312 | + // But you add the wrapper name Object, use (n[2] + (3*n[1])) % 16. | |
| 313 | + private static final Wrapper[] FROM_PRIM = new Wrapper[16]; | |
| 314 | + private static final Wrapper[] FROM_WRAP = new Wrapper[16]; | |
| 315 | + private static final Wrapper[] FROM_CHAR = new Wrapper[16]; | |
| 316 | + private static int hashPrim(Class<?> x) { | |
| 317 | + String xn = x.getName(); | |
| 318 | + if (xn.length() < 3) return 0; | |
| 319 | + return (xn.charAt(0) + xn.charAt(2)) % 16; | |
| 320 | + } | |
| 321 | + private static int hashWrap(Class<?> x) { | |
| 322 | + String xn = x.getName(); | |
| 323 | + final int offset = 10; assert(offset == "java.lang.".length()); | |
| 324 | + if (xn.length() < offset+3) return 0; | |
| 325 | + return (3*xn.charAt(offset+1) + xn.charAt(offset+2)) % 16; | |
| 326 | + } | |
| 327 | + private static int hashChar(char x) { | |
| 328 | + return (x + (x >> 1)) % 16; | |
| 329 | + } | |
| 330 | + static { | |
| 331 | + for (Wrapper w : values()) { | |
| 332 | + int pi = hashPrim(w.primitiveType); | |
| 333 | + int wi = hashWrap(w.wrapperType); | |
| 334 | + int ci = hashChar(w.basicTypeChar); | |
| 335 | + assert(FROM_PRIM[pi] == null); | |
| 336 | + assert(FROM_WRAP[wi] == null); | |
| 337 | + assert(FROM_CHAR[ci] == null); | |
| 338 | + FROM_PRIM[pi] = w; | |
| 339 | + FROM_WRAP[wi] = w; | |
| 340 | + FROM_CHAR[ci] = w; | |
| 341 | + } | |
| 342 | + //assert(jdk.sun.invoke.util.WrapperTest.test(false)); | |
| 343 | + } | |
| 344 | + | |
| 345 | + /** What is the primitive type wrapped by this wrapper? */ | |
| 346 | + public Class<?> primitiveType() { return primitiveType; } | |
| 347 | + | |
| 348 | + /** What is the wrapper type for this wrapper? */ | |
| 349 | + public Class<?> wrapperType() { return wrapperType; } | |
| 350 | + | |
| 351 | + /** What is the wrapper type for this wrapper? | |
| 352 | + * Otherwise, the example type must be the wrapper type, | |
| 353 | + * or the corresponding primitive type. | |
| 354 | + * (For {@code OBJECT}, the example type can be any non-primitive, | |
| 355 | + * and is normalized to {@code Object.class}.) | |
| 356 | + * The resulting class type has the same type parameter. | |
| 357 | + */ | |
| 358 | + public <T> Class<T> wrapperType(Class<T> exampleType) { | |
| 359 | + if (exampleType == wrapperType) { | |
| 360 | + return exampleType; | |
| 361 | + } else if (exampleType == primitiveType || | |
| 362 | + wrapperType == Object.class || | |
| 363 | + exampleType.isInterface()) { | |
| 364 | + return forceType(wrapperType, exampleType); | |
| 365 | + } | |
| 366 | + throw newClassCastException(exampleType, primitiveType); | |
| 367 | + } | |
| 368 | + | |
| 369 | + private static ClassCastException newClassCastException(Class<?> actual, Class<?> expected) { | |
| 370 | + return new ClassCastException(actual + " is not compatible with " + expected); | |
| 371 | + } | |
| 372 | + | |
| 373 | + /** If {@code type} is a primitive type, return the corresponding | |
| 374 | + * wrapper type, else return {@code type} unchanged. | |
| 375 | + */ | |
| 376 | + public static <T> Class<T> asWrapperType(Class<T> type) { | |
| 377 | + if (type.isPrimitive()) { | |
| 378 | + return forPrimitiveType(type).wrapperType(type); | |
| 379 | + } | |
| 380 | + return type; | |
| 381 | + } | |
| 382 | + | |
| 383 | + /** If {@code type} is a wrapper type, return the corresponding | |
| 384 | + * primitive type, else return {@code type} unchanged. | |
| 385 | + */ | |
| 386 | + public static <T> Class<T> asPrimitiveType(Class<T> type) { | |
| 387 | + Wrapper w = findWrapperType(type); | |
| 388 | + if (w != null) { | |
| 389 | + return forceType(w.primitiveType(), type); | |
| 390 | + } | |
| 391 | + return type; | |
| 392 | + } | |
| 393 | + | |
| 394 | + /** Query: Is the given type a wrapper, such as {@code Integer} or {@code Void}? */ | |
| 395 | + public static boolean isWrapperType(Class<?> type) { | |
| 396 | + return findWrapperType(type) != null; | |
| 397 | + } | |
| 398 | + | |
| 399 | + /** Query: Is the given type a primitive, such as {@code int} or {@code void}? */ | |
| 400 | + public static boolean isPrimitiveType(Class<?> type) { | |
| 401 | + return type.isPrimitive(); | |
| 402 | + } | |
| 403 | + | |
| 404 | + /** What is the bytecode signature character for this type? | |
| 405 | + * All non-primitives, including array types, report as 'L', the signature character for references. | |
| 406 | + */ | |
| 407 | + public static char basicTypeChar(Class<?> type) { | |
| 408 | + if (!type.isPrimitive()) | |
| 409 | + return 'L'; | |
| 410 | + else | |
| 411 | + return forPrimitiveType(type).basicTypeChar(); | |
| 412 | + } | |
| 413 | + | |
| 414 | + /** What is the bytecode signature character for this wrapper's | |
| 415 | + * primitive type? | |
| 416 | + */ | |
| 417 | + public char basicTypeChar() { return basicTypeChar; } | |
| 418 | + | |
| 419 | + /** What is the simple name of the wrapper type? | |
| 420 | + */ | |
| 421 | + public String simpleName() { return simpleName; } | |
| 422 | + | |
| 423 | +// /** Wrap a value in the given type, which may be either a primitive or wrapper type. | |
| 424 | +// * Performs standard primitive conversions, including truncation and float conversions. | |
| 425 | +// */ | |
| 426 | +// public static <T> T wrap(Object x, Class<T> type) { | |
| 427 | +// return Wrapper.valueOf(type).cast(x, type); | |
| 428 | +// } | |
| 429 | + | |
| 430 | + /** Cast a wrapped value to the given type, which may be either a primitive or wrapper type. | |
| 431 | + * The given target type must be this wrapper's primitive or wrapper type. | |
| 432 | + * If this wrapper is OBJECT, the target type may also be an interface, perform no runtime check. | |
| 433 | + * Performs standard primitive conversions, including truncation and float conversions. | |
| 434 | + * The given type must be compatible with this wrapper. That is, it must either | |
| 435 | + * be the wrapper type (or a subtype, in the case of {@code OBJECT}) or else | |
| 436 | + * it must be the wrapper's primitive type. | |
| 437 | + * Primitive conversions are only performed if the given type is itself a primitive. | |
| 438 | + * @throws ClassCastException if the given type is not compatible with this wrapper | |
| 439 | + */ | |
| 440 | + public <T> T cast(Object x, Class<T> type) { | |
| 441 | + return convert(x, type, true); | |
| 442 | + } | |
| 443 | + | |
| 444 | + /** Convert a wrapped value to the given type. | |
| 445 | + * The given target type must be this wrapper's primitive or wrapper type. | |
| 446 | + * This is equivalent to {@link #cast}, except that it refuses to perform | |
| 447 | + * narrowing primitive conversions. | |
| 448 | + */ | |
| 449 | + public <T> T convert(Object x, Class<T> type) { | |
| 450 | + return convert(x, type, false); | |
| 451 | + } | |
| 452 | + | |
| 453 | + private <T> T convert(Object x, Class<T> type, boolean isCast) { | |
| 454 | + if (this == OBJECT) { | |
| 455 | + // If the target wrapper is OBJECT, just do a reference cast. | |
| 456 | + // If the target type is an interface, perform no runtime check. | |
| 457 | + // (This loophole is safe, and is allowed by the JVM verifier.) | |
| 458 | + // If the target type is a primitive, change it to a wrapper. | |
| 459 | + @SuppressWarnings("unchecked") | |
| 460 | + T result = (T) x; // unchecked warning is expected here | |
| 461 | + return result; | |
| 462 | + } | |
| 463 | + Class<T> wtype = wrapperType(type); | |
| 464 | + if (wtype.isInstance(x)) { | |
| 465 | + @SuppressWarnings("unchecked") | |
| 466 | + T result = (T) x; // unchecked warning is expected here | |
| 467 | + return result; | |
| 468 | + } | |
| 469 | + Class<?> sourceType = x.getClass(); // throw NPE if x is null | |
| 470 | + if (!isCast) { | |
| 471 | + Wrapper source = findWrapperType(sourceType); | |
| 472 | + if (source == null || !this.isConvertibleFrom(source)) { | |
| 473 | + throw newClassCastException(wtype, sourceType); | |
| 474 | + } | |
| 475 | + } | |
| 476 | + @SuppressWarnings("unchecked") | |
| 477 | + T result = (T) wrap(x); // unchecked warning is expected here | |
| 478 | + assert result.getClass() == wtype; | |
| 479 | + return result; | |
| 480 | + } | |
| 481 | + | |
| 482 | + /** Cast a reference type to another reference type. | |
| 483 | + * If the target type is an interface, perform no runtime check. | |
| 484 | + * (This loophole is safe, and is allowed by the JVM verifier.) | |
| 485 | + * If the target type is a primitive, change it to a wrapper. | |
| 486 | + */ | |
| 487 | + static <T> Class<T> forceType(Class<?> type, Class<T> exampleType) { | |
| 488 | + boolean z = (type == exampleType || | |
| 489 | + type.isPrimitive() && forPrimitiveType(type) == findWrapperType(exampleType) || | |
| 490 | + exampleType.isPrimitive() && forPrimitiveType(exampleType) == findWrapperType(type) || | |
| 491 | + type == Object.class && !exampleType.isPrimitive()); | |
| 492 | + if (!z) | |
| 493 | + System.out.println(type+" <= "+exampleType); | |
| 494 | + assert(type == exampleType || | |
| 495 | + type.isPrimitive() && forPrimitiveType(type) == findWrapperType(exampleType) || | |
| 496 | + exampleType.isPrimitive() && forPrimitiveType(exampleType) == findWrapperType(type) || | |
| 497 | + type == Object.class && !exampleType.isPrimitive()); | |
| 498 | + @SuppressWarnings("unchecked") | |
| 499 | + Class<T> result = (Class<T>) type; // unchecked warning is expected here | |
| 500 | + return result; | |
| 501 | + } | |
| 502 | + | |
| 503 | + /** Wrap a value in this wrapper's type. | |
| 504 | + * Performs standard primitive conversions, including truncation and float conversions. | |
| 505 | + * Performs returns the unchanged reference for {@code OBJECT}. | |
| 506 | + * Returns null for {@code VOID}. | |
| 507 | + * Returns a zero value for a null input. | |
| 508 | + * @throws ClassCastException if this wrapper is numeric and the operand | |
| 509 | + * is not a number, character, boolean, or null | |
| 510 | + */ | |
| 511 | + public Object wrap(Object x) { | |
| 512 | + // do non-numeric wrappers first | |
| 513 | + switch (basicTypeChar) { | |
| 514 | + case 'L': return x; | |
| 515 | + case 'V': return null; | |
| 516 | + } | |
| 517 | + Number xn = numberValue(x); | |
| 518 | + switch (basicTypeChar) { | |
| 519 | + case 'I': return Integer.valueOf(xn.intValue()); | |
| 520 | + case 'J': return Long.valueOf(xn.longValue()); | |
| 521 | + case 'F': return Float.valueOf(xn.floatValue()); | |
| 522 | + case 'D': return Double.valueOf(xn.doubleValue()); | |
| 523 | + case 'S': return Short.valueOf((short) xn.intValue()); | |
| 524 | + case 'B': return Byte.valueOf((byte) xn.intValue()); | |
| 525 | + case 'C': return Character.valueOf((char) xn.intValue()); | |
| 526 | + case 'Z': return Boolean.valueOf(boolValue(xn.longValue())); | |
| 527 | + } | |
| 528 | + throw new InternalError("bad wrapper"); | |
| 529 | + } | |
| 530 | + | |
| 531 | + /** Wrap a value (an int or smaller value) in this wrapper's type. | |
| 532 | + * Performs standard primitive conversions, including truncation and float conversions. | |
| 533 | + * Produces an {@code Integer} for {@code OBJECT}, although the exact type | |
| 534 | + * of the operand is not known. | |
| 535 | + * Returns null for {@code VOID}. | |
| 536 | + */ | |
| 537 | + public Object wrap(int x) { | |
| 538 | + if (basicTypeChar == 'L') return (Integer)x; | |
| 539 | + switch (basicTypeChar) { | |
| 540 | + case 'L': throw newIllegalArgumentException("cannot wrap to object type"); | |
| 541 | + case 'V': return null; | |
| 542 | + case 'I': return Integer.valueOf((int)x); | |
| 543 | + case 'J': return Long.valueOf(x); | |
| 544 | + case 'F': return Float.valueOf(x); | |
| 545 | + case 'D': return Double.valueOf(x); | |
| 546 | + case 'S': return Short.valueOf((short) x); | |
| 547 | + case 'B': return Byte.valueOf((byte) x); | |
| 548 | + case 'C': return Character.valueOf((char) x); | |
| 549 | + case 'Z': return Boolean.valueOf(boolValue(x)); | |
| 550 | + } | |
| 551 | + throw new InternalError("bad wrapper"); | |
| 552 | + } | |
| 553 | + | |
| 554 | + /** Wrap a value (a long or smaller value) in this wrapper's type. | |
| 555 | + * Does not perform floating point conversion. | |
| 556 | + * Produces a {@code Long} for {@code OBJECT}, although the exact type | |
| 557 | + * of the operand is not known. | |
| 558 | + * Returns null for {@code VOID}. | |
| 559 | + */ | |
| 560 | + public Object wrapRaw(long x) { | |
| 561 | + switch (basicTypeChar) { | |
| 562 | + case 'F': return Float.valueOf(Float.intBitsToFloat((int)x)); | |
| 563 | + case 'D': return Double.valueOf(Double.longBitsToDouble(x)); | |
| 564 | + case 'L': // same as 'J': | |
| 565 | + case 'J': return (Long) x; | |
| 566 | + } | |
| 567 | + // Other wrapping operations are just the same, given that the | |
| 568 | + // operand is already promoted to an int. | |
| 569 | + return wrap((int)x); | |
| 570 | + } | |
| 571 | + | |
| 572 | + /** Produce bitwise value which encodes the given wrapped value. | |
| 573 | + * Does not perform floating point conversion. | |
| 574 | + * Returns zero for {@code VOID}. | |
| 575 | + */ | |
| 576 | + public long unwrapRaw(Object x) { | |
| 577 | + switch (basicTypeChar) { | |
| 578 | + case 'F': return Float.floatToRawIntBits((Float) x); | |
| 579 | + case 'D': return Double.doubleToRawLongBits((Double) x); | |
| 580 | + | |
| 581 | + case 'L': throw newIllegalArgumentException("cannot unwrap from sobject type"); | |
| 582 | + case 'V': return 0; | |
| 583 | + case 'I': return (int)(Integer) x; | |
| 584 | + case 'J': return (long)(Long) x; | |
| 585 | + case 'S': return (short)(Short) x; | |
| 586 | + case 'B': return (byte)(Byte) x; | |
| 587 | + case 'C': return (char)(Character) x; | |
| 588 | + case 'Z': return (boolean)(Boolean) x ? 1 : 0; | |
| 589 | + } | |
| 590 | + throw new InternalError("bad wrapper"); | |
| 591 | + } | |
| 592 | + | |
| 593 | + /** Report what primitive type holds this guy's raw value. */ | |
| 594 | + public Class<?> rawPrimitiveType() { | |
| 595 | + return rawPrimitive().primitiveType(); | |
| 596 | + } | |
| 597 | + | |
| 598 | + /** Report, as a wrapper, what primitive type holds this guy's raw value. | |
| 599 | + * Returns self for INT, LONG, OBJECT; returns LONG for DOUBLE, | |
| 600 | + * else returns INT. | |
| 601 | + */ | |
| 602 | + public Wrapper rawPrimitive() { | |
| 603 | + switch (basicTypeChar) { | |
| 604 | + case 'S': case 'B': | |
| 605 | + case 'C': case 'Z': | |
| 606 | + case 'V': | |
| 607 | + case 'F': | |
| 608 | + return INT; | |
| 609 | + case 'D': | |
| 610 | + return LONG; | |
| 611 | + } | |
| 612 | + return this; | |
| 613 | + } | |
| 614 | + | |
| 615 | + private static Number numberValue(Object x) { | |
| 616 | + if (x instanceof Number) return (Number)x; | |
| 617 | + if (x instanceof Character) return (int)(Character)x; | |
| 618 | + if (x instanceof Boolean) return (Boolean)x ? 1 : 0; | |
| 619 | + // Remaining allowed case of void: Must be a null reference. | |
| 620 | + return (Number)x; | |
| 621 | + } | |
| 622 | + | |
| 623 | + private static boolean boolValue(long bits) { | |
| 624 | + bits &= 1; // simple 31-bit zero extension | |
| 625 | + return (bits != 0); | |
| 626 | + } | |
| 627 | + | |
| 628 | + private static RuntimeException newIllegalArgumentException(String message, Object x) { | |
| 629 | + return newIllegalArgumentException(message + x); | |
| 630 | + } | |
| 631 | + private static RuntimeException newIllegalArgumentException(String message) { | |
| 632 | + return new IllegalArgumentException(message); | |
| 633 | + } | |
| 634 | + | |
| 635 | + // primitive array support | |
| 636 | + public Object makeArray(int len) { | |
| 637 | + return java.lang.reflect.Array.newInstance(primitiveType, len); | |
| 638 | + } | |
| 639 | + public Class<?> arrayType() { | |
| 640 | + return emptyArray.getClass(); | |
| 641 | + } | |
| 642 | + public void copyArrayUnboxing(Object[] values, int vpos, Object a, int apos, int length) { | |
| 643 | + if (a.getClass() != arrayType()) | |
| 644 | + arrayType().cast(a); // throw NPE or CCE if bad type | |
| 645 | + for (int i = 0; i < length; i++) { | |
| 646 | + Object value = values[i+vpos]; | |
| 647 | + value = convert(value, primitiveType); | |
| 648 | + java.lang.reflect.Array.set(a, i+apos, value); | |
| 649 | + } | |
| 650 | + } | |
| 651 | + public void copyArrayBoxing(Object a, int apos, Object[] values, int vpos, int length) { | |
| 652 | + if (a.getClass() != arrayType()) | |
| 653 | + arrayType().cast(a); // throw NPE or CCE if bad type | |
| 654 | + for (int i = 0; i < length; i++) { | |
| 655 | + Object value = java.lang.reflect.Array.get(a, i+apos); | |
| 656 | + //Already done: value = convert(value, primitiveType); | |
| 657 | + assert(value.getClass() == wrapperType); | |
| 658 | + values[i+vpos] = value; | |
| 659 | + } | |
| 660 | + } | |
| 661 | +} | |