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