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 jdk.experimental.value;
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.lang.reflect.Field;
33 import java.lang.reflect.Modifier;
34 import java.util.Arrays;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.Objects;
38 import java.util.concurrent.ConcurrentHashMap;
39 import java.util.function.Consumer;
40 import java.util.function.Supplier;
41 import java.util.stream.Collectors;
42 import java.util.stream.IntStream;
43 import java.util.stream.Stream;
44
45 import jdk.experimental.bytecode.AnnotationsBuilder.Kind;
46 import jdk.experimental.bytecode.Flag;
47 import jdk.experimental.bytecode.Opcode;
48 import jdk.experimental.value.MethodHandleBuilder.IsolatedMethodBuilder;
49 import jdk.experimental.value.MethodHandleBuilder.MethodHandleCodeBuilder;
50 import jdk.experimental.value.ValueType.ValueHandleKind.ValueHandleKey;
51 import jdk.experimental.bytecode.MacroCodeBuilder.CondKind;
52 import jdk.experimental.bytecode.TypeTag;
53 import jdk.internal.misc.Unsafe;
54 import sun.invoke.util.BytecodeDescriptor;
55 import sun.invoke.util.Wrapper;
56 import valhalla.shady.MinimalValueTypes_1_0;
57
58 // Rough place holder just now...
59 public class ValueType<T> {
60
61 enum ValueHandleKind {
62 BOX("box"),
63 UNBOX("unbox"),
64 DEFAULT("defaultValueConstant"),
65 EQ("substitutabilityTest"),
66 HASH("substitutabilityHashCode"),
67 ARRAYLENGTH("arrayLength"),
68 WITHER("findWither", Lookup.class, String.class, Class.class) {
69 @Override
70 ValueHandleKey key(Object fieldName) {
71 return new ValueHandleKey(this, fieldName);
72 }
73 },
74 UNREFLECT_WITHERS("unreflectWithers", Lookup.class, boolean.class, Field[].class) {
75 @Override
76 ValueHandleKey key(Object fields) {
77 return new ValueHandleKey(this, fields);
78 }
79 },
81 VALOAD("arrayGetter"),
82 VASTORE("arraySetter"),
83 MULTINEWARRAY("newMultiArray", int.class) {
84 @Override
85 ValueHandleKey key(Object dims) {
86 return new ValueHandleKey(this, dims);
87 }
88 },
89 IDENTITY("identity"),
90 GETTER("findGetter", Lookup.class, String.class, Class.class) {
91 @Override
92 ValueHandleKey key(Object fieldName) {
93 return new ValueHandleKey(this, fieldName);
94 }
95 };
96
97 final MethodHandle handle;
98
99 ValueHandleKind(String handleName, Class<?>... argtypes) {
100 try {
101 this.handle = MethodHandles.lookup().findVirtual(ValueType.class, handleName, MethodType.methodType(MethodHandle.class, argtypes));
102 } catch (ReflectiveOperationException ex) {
103 throw new IllegalArgumentException("Cannot initialize value handle key for: " + handleName);
104 }
105 }
106
107 String handleName() {
108 return MethodHandles.lookup().revealDirect(handle).getName();
109 }
110
111 MethodType handleType() {
112 return MethodHandles.lookup().revealDirect(handle).getMethodType();
113 }
114
115 ValueHandleKey key() {
116 return new ValueHandleKey(this, null);
117 }
118
119 ValueHandleKey key(Object optArg) {
120 throw new IllegalStateException();
121 }
142
143 @Override
144 public int hashCode() {
145 return Objects.hashCode(kind) * 31 + Objects.hashCode(optArg);
146 }
147 }
148 }
149
150 private static final Lookup IMPL_LOOKUP;
151
152 static {
153 try {
154 Field f = Lookup.class.getDeclaredField("IMPL_LOOKUP");
155 f.setAccessible(true);
156 IMPL_LOOKUP = (Lookup)f.get(null);
157 } catch (ReflectiveOperationException ex) {
158 throw new AssertionError(ex);
159 }
160 }
161
162 private static final ConcurrentHashMap<Class<?>, ValueType<?>> BOX_TO_VT = new ConcurrentHashMap<>();
163
164 public static boolean classHasValueType(Class<?> x) {
165 if (!MinimalValueTypes_1_0.isValueCapable(x)) {
166 return false;
167 }
168 return MinimalValueTypes_1_0.getValueTypeClass(x) != null;
169 }
170
171 @SuppressWarnings("unchecked")
172 public static <T> ValueType<T> forClass(Class<T> x) {
173 if (!MinimalValueTypes_1_0.isValueCapable(x)) {
174 throw new IllegalArgumentException("Class " + x + " not a value capable class");
175 }
176
177 ValueType<T> vt = (ValueType<T>) BOX_TO_VT.get(x);
178 if (vt != null) {
179 return vt;
180 }
181
182 Class<T> valueClass = (Class<T>) MinimalValueTypes_1_0.getValueTypeClass(x);
183 vt = new ValueType<T>(x, valueClass);
184 ValueType<T> old = (ValueType<T>) BOX_TO_VT.putIfAbsent(x, vt);
185 if (old != null) {
186 vt = old;
187 }
188 return vt;
189 }
190
191 public static <T> ValueType<T> make(Lookup lookup, String name, String[] fieldNames, Class<?>... fieldTypes) throws ReflectiveOperationException {
192 if (fieldNames.length != fieldTypes.length) {
193 throw new IllegalArgumentException("Field names length and field types length must match");
194 }
195 if (!(fieldNames.length > 0)) {
196 throw new IllegalArgumentException("Field length must be greater than zero");
197 }
198 IsolatedMethodBuilder builder = new IsolatedMethodBuilder(name, lookup);
199 builder.withMajorVersion(53)
200 .withMinorVersion(0)
201 .withSuperclass(Object.class)
202 .withFlags(Flag.ACC_FINAL)
203 .withAnnotation(Kind.RUNTIME_VISIBLE, "Ljvm/internal/value/ValueCapableClass;");
204 //add fields
205 for (int i = 0 ; i < fieldNames.length ; i++) {
206 builder.withField(fieldNames[i], BytecodeDescriptor.unparse(fieldTypes[i]), F -> F.withFlags(Flag.ACC_FINAL));
207 }
208 //add constructor
209 String ctype = BytecodeDescriptor.unparseMethod(void.class, fieldTypes);
210 builder.withMethod("<init>", ctype, M -> M.withCode(MethodHandleCodeBuilder::new, C -> {
211 C.aload_0().invokespecial(Object.class, "<init>", "()V", false);
212 int l = 1;
213 for (int i = 0 ; i < fieldNames.length ; i++) {
214 String fType = BytecodeDescriptor.unparse(fieldTypes[i]);
215 C.aload_0().load(l).putfield(builder.thisClass(), fieldNames[i], fType);
216 l += Wrapper.forBasicType(fieldTypes[i]).stackSlots();
217 }
218 C.return_();
219 }));
220 //add equals and hashCode
221 builder.withMethod("equals", "(Ljava/lang/Object;)Z", M ->
222 M.withFlags(Flag.ACC_PUBLIC).withCode(MethodHandleCodeBuilder::new,
223 C -> substitutabilityTestBuilder(true, builder.thisClass(), FieldInfo.stream(fieldNames, fieldTypes), C)));
224 builder.withMethod("hashCode", "()I", M ->
225 M.withFlags(Flag.ACC_PUBLIC).withCode(MethodHandleCodeBuilder::new,
226 C -> substitutabilityHashCodeBuilder(builder.thisClass(), FieldInfo.stream(fieldNames, fieldTypes), C)));
227 byte[] barr = builder.build();
228 MinimalValueTypes_1_0.maybeDump(name, barr);
229 @SuppressWarnings("unchecked")
230 Class<T> vtClass = (Class<T>)lookup.defineClass(barr);
231 return forClass(vtClass);
232 }
233
234 private Lookup boxLookup;
235 private Lookup valueLookup;
236 private Map<ValueHandleKind.ValueHandleKey, MethodHandle> handleMap = new ConcurrentHashMap<>();
237
238 private ValueType(Class<T> boxClass, Class<T> valueClass) {
239 this.boxLookup = IMPL_LOOKUP.in(boxClass);
240 this.valueLookup = IMPL_LOOKUP.in(valueClass);
241 }
242
243 @SuppressWarnings("unchecked")
244 public Class<T> boxClass() {
245 return (Class<T>)boxLookup.lookupClass();
246 }
247
248 public Class<?> sourceClass() {
249 return boxClass();
250 }
251
252 public Class<?> valueClass() {
253 return valueLookup.lookupClass();
254 }
255
256 public Class<?> arrayValueClass() {
257 return arrayValueClass(1);
258 }
497 () -> MethodType.methodType(valueClass(), valueClass()),
498 C -> C.vload(0).vreturn());
499 }
500
501 public MethodHandle findGetter(Lookup lookup, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
502 //force access-check
503 lookup.findGetter(boxClass(), name, type);
504
505 ValueHandleKey key = ValueHandleKind.GETTER.key(new FieldInfo(name, type));
506 String fieldType = BytecodeDescriptor.unparse(type);
507 return getOrLoad(boxLookup, key,
508 () -> MethodType.methodType(type, valueClass()),
509 C -> C.vload(0).getfield(valueClass(), name, fieldType).return_(fieldType));
510 }
511
512 private static <T extends MethodHandleCodeBuilder<T>> T valueHandleBuilder(Class<?> dvt, ValueHandleKey key, MethodHandleCodeBuilder<T> C) {
513 MethodType mt = key.kind.handleType();
514 if (mt.parameterCount() > 0) {
515 throw new AssertionError("Non-nilary handle builders not supported yet");
516 }
517 return C.vbox(MinimalValueTypes_1_0.getValueCapableClass(dvt))
518 .invokevirtual(Object.class, "getClass", "()Ljava/lang/Class;", false)
519 .invokestatic(ValueType.class, "forClass",
520 MethodType.methodType(ValueType.class, Class.class).toMethodDescriptorString(), false)
521 .invokevirtual(ValueType.class, key.kind.handleName(), key.kind.handleType().toMethodDescriptorString(), false);
522 }
523
524 private MethodHandle getOrLoad(Lookup lookup, ValueHandleKey key, Supplier<MethodType> typeSupplier, Consumer<? super MethodHandleCodeBuilder<?>> codeBuilder) {
525 MethodHandle result = handleMap.get(key);
526 if (result == null) {
527 String handleDebugName = sourceClass().getName() + "_" + key.kind.handleName();
528 result = MethodHandleBuilder.loadCode(lookup, handleDebugName, typeSupplier.get(), codeBuilder);
529 handleMap.put(key, result);
530 }
531 return result;
532 }
533
534 boolean isValueField(Field f) {
535 return (f.getModifiers() & (Modifier.FINAL | Modifier.STATIC)) == Modifier.FINAL;
536 }
537
538 public Field[] valueFields() {
539 return Stream.of(sourceClass().getDeclaredFields())
540 .filter(this::isValueField)
541 .toArray(Field[]::new);
|
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 valhalla.shady;
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.lang.reflect.Field;
33 import java.lang.reflect.Modifier;
34 import java.util.Arrays;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.Objects;
38 import java.util.concurrent.ConcurrentHashMap;
39 import java.util.function.Consumer;
40 import java.util.function.Supplier;
41 import java.util.stream.Collectors;
42 import java.util.stream.IntStream;
43 import java.util.stream.Stream;
44
45 import jdk.experimental.bytecode.AnnotationsBuilder.Kind;
46 import jdk.experimental.bytecode.Flag;
47 import jdk.experimental.bytecode.Opcode;
48 import jdk.experimental.value.MethodHandleBuilder.IsolatedMethodBuilder;
49 import jdk.experimental.value.MethodHandleBuilder.MethodHandleCodeBuilder;
50 import jdk.experimental.value.MethodHandleBuilder;
51 import jdk.experimental.bytecode.MacroCodeBuilder.CondKind;
52 import jdk.experimental.bytecode.TypeTag;
53 import sun.invoke.util.BytecodeDescriptor;
54 import sun.invoke.util.Wrapper;
55 import valhalla.shady.ValueTypeHolder.ValueHandleKind.ValueHandleKey;
56
57 // Rough place holder just now...
58 public class ValueTypeHolder<T> {
59
60 enum ValueHandleKind {
61 BOX("box"),
62 UNBOX("unbox"),
63 DEFAULT("defaultValueConstant"),
64 EQ("substitutabilityTest"),
65 HASH("substitutabilityHashCode"),
66 ARRAYLENGTH("arrayLength"),
67 WITHER("findWither", Lookup.class, String.class, Class.class) {
68 @Override
69 ValueHandleKey key(Object fieldName) {
70 return new ValueHandleKey(this, fieldName);
71 }
72 },
73 UNREFLECT_WITHERS("unreflectWithers", Lookup.class, boolean.class, Field[].class) {
74 @Override
75 ValueHandleKey key(Object fields) {
76 return new ValueHandleKey(this, fields);
77 }
78 },
80 VALOAD("arrayGetter"),
81 VASTORE("arraySetter"),
82 MULTINEWARRAY("newMultiArray", int.class) {
83 @Override
84 ValueHandleKey key(Object dims) {
85 return new ValueHandleKey(this, dims);
86 }
87 },
88 IDENTITY("identity"),
89 GETTER("findGetter", Lookup.class, String.class, Class.class) {
90 @Override
91 ValueHandleKey key(Object fieldName) {
92 return new ValueHandleKey(this, fieldName);
93 }
94 };
95
96 final MethodHandle handle;
97
98 ValueHandleKind(String handleName, Class<?>... argtypes) {
99 try {
100 this.handle = MethodHandles.lookup().findVirtual(ValueTypeHolder.class, handleName, MethodType.methodType(MethodHandle.class, argtypes));
101 } catch (ReflectiveOperationException ex) {
102 throw new IllegalArgumentException("Cannot initialize value handle key for: " + handleName);
103 }
104 }
105
106 String handleName() {
107 return MethodHandles.lookup().revealDirect(handle).getName();
108 }
109
110 MethodType handleType() {
111 return MethodHandles.lookup().revealDirect(handle).getMethodType();
112 }
113
114 ValueHandleKey key() {
115 return new ValueHandleKey(this, null);
116 }
117
118 ValueHandleKey key(Object optArg) {
119 throw new IllegalStateException();
120 }
141
142 @Override
143 public int hashCode() {
144 return Objects.hashCode(kind) * 31 + Objects.hashCode(optArg);
145 }
146 }
147 }
148
149 private static final Lookup IMPL_LOOKUP;
150
151 static {
152 try {
153 Field f = Lookup.class.getDeclaredField("IMPL_LOOKUP");
154 f.setAccessible(true);
155 IMPL_LOOKUP = (Lookup)f.get(null);
156 } catch (ReflectiveOperationException ex) {
157 throw new AssertionError(ex);
158 }
159 }
160
161 public static <T> Class<T> makeValueTypeClass(Lookup lookup, String name, String[] fieldNames, Class<?>... fieldTypes) throws ReflectiveOperationException {
162 if (fieldNames.length != fieldTypes.length) {
163 throw new IllegalArgumentException("Field names length and field types length must match");
164 }
165 if (!(fieldNames.length > 0)) {
166 throw new IllegalArgumentException("Field length must be greater than zero");
167 }
168 IsolatedMethodBuilder builder = new IsolatedMethodBuilder(name, lookup);
169 builder.withMajorVersion(53)
170 .withMinorVersion(0)
171 .withSuperclass(Object.class)
172 .withFlags(Flag.ACC_FINAL)
173 .withAnnotation(Kind.RUNTIME_VISIBLE, MinimalValueTypes_1_0.DERIVE_VALUE_TYPE_DESC);
174 //add fields
175 for (int i = 0 ; i < fieldNames.length ; i++) {
176 builder.withField(fieldNames[i], BytecodeDescriptor.unparse(fieldTypes[i]), F -> F.withFlags(Flag.ACC_FINAL));
177 }
178 //add constructor
179 String ctype = BytecodeDescriptor.unparseMethod(void.class, fieldTypes);
180 builder.withMethod("<init>", ctype, M -> M.withCode(MethodHandleCodeBuilder::new, C -> {
181 C.aload_0().invokespecial(Object.class, "<init>", "()V", false);
182 int l = 1;
183 for (int i = 0 ; i < fieldNames.length ; i++) {
184 String fType = BytecodeDescriptor.unparse(fieldTypes[i]);
185 C.aload_0().load(l).putfield(builder.thisClass(), fieldNames[i], fType);
186 l += Wrapper.forBasicType(fieldTypes[i]).stackSlots();
187 }
188 C.return_();
189 }));
190 //add equals and hashCode
191 builder.withMethod("equals", "(Ljava/lang/Object;)Z", M ->
192 M.withFlags(Flag.ACC_PUBLIC).withCode(MethodHandleCodeBuilder::new,
193 C -> substitutabilityTestBuilder(true, builder.thisClass(), FieldInfo.stream(fieldNames, fieldTypes), C)));
194 builder.withMethod("hashCode", "()I", M ->
195 M.withFlags(Flag.ACC_PUBLIC).withCode(MethodHandleCodeBuilder::new,
196 C -> substitutabilityHashCodeBuilder(builder.thisClass(), FieldInfo.stream(fieldNames, fieldTypes), C)));
197 byte[] barr = builder.build();
198 MinimalValueTypes_1_0.maybeDump(name, barr);
199 @SuppressWarnings("unchecked")
200 Class<T> vtClass = (Class<T>)lookup.defineClass(barr);
201 return vtClass;
202 }
203
204 private Lookup boxLookup;
205 private Lookup valueLookup;
206 private Map<ValueHandleKind.ValueHandleKey, MethodHandle> handleMap = new ConcurrentHashMap<>();
207
208 ValueTypeHolder(Class<T> boxClass, Class<T> valueClass) {
209 this.boxLookup = IMPL_LOOKUP.in(boxClass);
210 this.valueLookup = IMPL_LOOKUP.in(valueClass);
211 }
212
213 @SuppressWarnings("unchecked")
214 public Class<T> boxClass() {
215 return (Class<T>)boxLookup.lookupClass();
216 }
217
218 public Class<?> sourceClass() {
219 return boxClass();
220 }
221
222 public Class<?> valueClass() {
223 return valueLookup.lookupClass();
224 }
225
226 public Class<?> arrayValueClass() {
227 return arrayValueClass(1);
228 }
467 () -> MethodType.methodType(valueClass(), valueClass()),
468 C -> C.vload(0).vreturn());
469 }
470
471 public MethodHandle findGetter(Lookup lookup, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
472 //force access-check
473 lookup.findGetter(boxClass(), name, type);
474
475 ValueHandleKey key = ValueHandleKind.GETTER.key(new FieldInfo(name, type));
476 String fieldType = BytecodeDescriptor.unparse(type);
477 return getOrLoad(boxLookup, key,
478 () -> MethodType.methodType(type, valueClass()),
479 C -> C.vload(0).getfield(valueClass(), name, fieldType).return_(fieldType));
480 }
481
482 private static <T extends MethodHandleCodeBuilder<T>> T valueHandleBuilder(Class<?> dvt, ValueHandleKey key, MethodHandleCodeBuilder<T> C) {
483 MethodType mt = key.kind.handleType();
484 if (mt.parameterCount() > 0) {
485 throw new AssertionError("Non-nilary handle builders not supported yet");
486 }
487 Class<?> vtSupportClass = MinimalValueTypes_1_0.getIncubatorValueTypeClass();
488 return C.vbox(MinimalValueTypes_1_0.getValueCapableClass(dvt))
489 .invokevirtual(Object.class, "getClass", "()Ljava/lang/Class;", false)
490 .invokestatic(vtSupportClass, "forClass",
491 MethodType.methodType(vtSupportClass, Class.class).toMethodDescriptorString(), false)
492 .invokevirtual(vtSupportClass, key.kind.handleName(), key.kind.handleType().toMethodDescriptorString(), false);
493 }
494
495 private MethodHandle getOrLoad(Lookup lookup, ValueHandleKey key, Supplier<MethodType> typeSupplier, Consumer<? super MethodHandleCodeBuilder<?>> codeBuilder) {
496 MethodHandle result = handleMap.get(key);
497 if (result == null) {
498 String handleDebugName = sourceClass().getName() + "_" + key.kind.handleName();
499 result = MethodHandleBuilder.loadCode(lookup, handleDebugName, typeSupplier.get(), codeBuilder);
500 handleMap.put(key, result);
501 }
502 return result;
503 }
504
505 boolean isValueField(Field f) {
506 return (f.getModifiers() & (Modifier.FINAL | Modifier.STATIC)) == Modifier.FINAL;
507 }
508
509 public Field[] valueFields() {
510 return Stream.of(sourceClass().getDeclaredFields())
511 .filter(this::isValueField)
512 .toArray(Field[]::new);
|