47 * methods for record classes.
48 *
49 * @since 16
50 */
51 public class ObjectMethods {
52
53 private ObjectMethods() { }
54
55 private static final int MAX_STRING_CONCAT_SLOTS = 20;
56
57 private static final MethodHandle FALSE = MethodHandles.zero(boolean.class);
58 private static final MethodHandle TRUE = MethodHandles.constant(boolean.class, true);
59 private static final MethodHandle ZERO = MethodHandles.zero(int.class);
60 private static final MethodHandle CLASS_IS_INSTANCE;
61 private static final MethodHandle OBJECTS_EQUALS;
62 private static final MethodHandle OBJECTS_HASHCODE;
63 private static final MethodHandle OBJECTS_TOSTRING;
64 private static final MethodHandle OBJECT_EQ;
65 private static final MethodHandle HASH_COMBINER;
66
67 private static final HashMap<Class<?>, MethodHandle> primitiveEquals = new HashMap<>();
68 private static final HashMap<Class<?>, MethodHandle> primitiveHashers = new HashMap<>();
69 private static final HashMap<Class<?>, MethodHandle> primitiveToString = new HashMap<>();
70
71 static {
72 try {
73 Class<ObjectMethods> OBJECT_METHODS_CLASS = ObjectMethods.class;
74 MethodHandles.Lookup publicLookup = MethodHandles.publicLookup();
75 MethodHandles.Lookup lookup = MethodHandles.lookup();
76
77 CLASS_IS_INSTANCE = publicLookup.findVirtual(Class.class, "isInstance",
78 MethodType.methodType(boolean.class, Object.class));
79 OBJECTS_EQUALS = publicLookup.findStatic(Objects.class, "equals",
80 MethodType.methodType(boolean.class, Object.class, Object.class));
81 OBJECTS_HASHCODE = publicLookup.findStatic(Objects.class, "hashCode",
82 MethodType.methodType(int.class, Object.class));
83 OBJECTS_TOSTRING = publicLookup.findStatic(Objects.class, "toString",
84 MethodType.methodType(String.class, Object.class));
85
86 OBJECT_EQ = lookup.findStatic(OBJECT_METHODS_CLASS, "eq",
87 MethodType.methodType(boolean.class, Object.class, Object.class));
374 * @return a call site if invoked by indy, or a method handle
375 * if invoked by a condy
376 * @throws IllegalArgumentException if the bootstrap arguments are invalid
377 * or inconsistent
378 * @throws NullPointerException if any argument is {@code null} or if any element
379 * in the {@code getters} array is {@code null}
380 * @throws Throwable if any exception is thrown during call site construction
381 */
382 public static Object bootstrap(MethodHandles.Lookup lookup, String methodName, TypeDescriptor type,
383 Class<?> recordClass,
384 String names,
385 MethodHandle... getters) throws Throwable {
386 requireNonNull(lookup);
387 requireNonNull(methodName);
388 requireNonNull(type);
389 requireNonNull(recordClass);
390 requireNonNull(names);
391 requireNonNull(getters);
392 Arrays.stream(getters).forEach(Objects::requireNonNull);
393 MethodType methodType;
394 if (type instanceof MethodType mt)
395 methodType = mt;
396 else {
397 methodType = null;
398 if (!MethodHandle.class.equals(type))
399 throw new IllegalArgumentException(type.toString());
400 }
401 List<MethodHandle> getterList = List.of(getters);
402 MethodHandle handle = switch (methodName) {
403 case "equals" -> {
404 if (methodType != null && !methodType.equals(MethodType.methodType(boolean.class, recordClass, Object.class)))
405 throw new IllegalArgumentException("Bad method type: " + methodType);
406 yield makeEquals(recordClass, getterList);
407 }
408 case "hashCode" -> {
409 if (methodType != null && !methodType.equals(MethodType.methodType(int.class, recordClass)))
410 throw new IllegalArgumentException("Bad method type: " + methodType);
411 yield makeHashCode(recordClass, getterList);
412 }
413 case "toString" -> {
414 if (methodType != null && !methodType.equals(MethodType.methodType(String.class, recordClass)))
415 throw new IllegalArgumentException("Bad method type: " + methodType);
416 List<String> nameList = "".equals(names) ? List.of() : List.of(names.split(";"));
417 if (nameList.size() != getterList.size())
418 throw new IllegalArgumentException("Name list and accessor list do not match");
419 yield makeToString(lookup, recordClass, getters, nameList);
420 }
421 default -> throw new IllegalArgumentException(methodName);
|
47 * methods for record classes.
48 *
49 * @since 16
50 */
51 public class ObjectMethods {
52
53 private ObjectMethods() { }
54
55 private static final int MAX_STRING_CONCAT_SLOTS = 20;
56
57 private static final MethodHandle FALSE = MethodHandles.zero(boolean.class);
58 private static final MethodHandle TRUE = MethodHandles.constant(boolean.class, true);
59 private static final MethodHandle ZERO = MethodHandles.zero(int.class);
60 private static final MethodHandle CLASS_IS_INSTANCE;
61 private static final MethodHandle OBJECTS_EQUALS;
62 private static final MethodHandle OBJECTS_HASHCODE;
63 private static final MethodHandle OBJECTS_TOSTRING;
64 private static final MethodHandle OBJECT_EQ;
65 private static final MethodHandle HASH_COMBINER;
66
67 /* package-private */
68 static final HashMap<Class<?>, MethodHandle> primitiveEquals = new HashMap<>();
69
70 private static final HashMap<Class<?>, MethodHandle> primitiveHashers = new HashMap<>();
71 private static final HashMap<Class<?>, MethodHandle> primitiveToString = new HashMap<>();
72
73 static {
74 try {
75 Class<ObjectMethods> OBJECT_METHODS_CLASS = ObjectMethods.class;
76 MethodHandles.Lookup publicLookup = MethodHandles.publicLookup();
77 MethodHandles.Lookup lookup = MethodHandles.lookup();
78
79 CLASS_IS_INSTANCE = publicLookup.findVirtual(Class.class, "isInstance",
80 MethodType.methodType(boolean.class, Object.class));
81 OBJECTS_EQUALS = publicLookup.findStatic(Objects.class, "equals",
82 MethodType.methodType(boolean.class, Object.class, Object.class));
83 OBJECTS_HASHCODE = publicLookup.findStatic(Objects.class, "hashCode",
84 MethodType.methodType(int.class, Object.class));
85 OBJECTS_TOSTRING = publicLookup.findStatic(Objects.class, "toString",
86 MethodType.methodType(String.class, Object.class));
87
88 OBJECT_EQ = lookup.findStatic(OBJECT_METHODS_CLASS, "eq",
89 MethodType.methodType(boolean.class, Object.class, Object.class));
376 * @return a call site if invoked by indy, or a method handle
377 * if invoked by a condy
378 * @throws IllegalArgumentException if the bootstrap arguments are invalid
379 * or inconsistent
380 * @throws NullPointerException if any argument is {@code null} or if any element
381 * in the {@code getters} array is {@code null}
382 * @throws Throwable if any exception is thrown during call site construction
383 */
384 public static Object bootstrap(MethodHandles.Lookup lookup, String methodName, TypeDescriptor type,
385 Class<?> recordClass,
386 String names,
387 MethodHandle... getters) throws Throwable {
388 requireNonNull(lookup);
389 requireNonNull(methodName);
390 requireNonNull(type);
391 requireNonNull(recordClass);
392 requireNonNull(names);
393 requireNonNull(getters);
394 Arrays.stream(getters).forEach(Objects::requireNonNull);
395 MethodType methodType;
396 if (type instanceof MethodType mt) {
397 methodType = mt;
398 if (mt.parameterType(0) != recordClass) {
399 throw new IllegalArgumentException("Bad method type: " + mt);
400 }
401 } else {
402 methodType = null;
403 if (!MethodHandle.class.equals(type))
404 throw new IllegalArgumentException(type.toString());
405 }
406 List<MethodHandle> getterList = List.of(getters);
407 for (MethodHandle getter : getterList) {
408 if (getter.type().parameterType(0) != recordClass) {
409 throw new IllegalArgumentException("Bad receiver type: " + getter);
410 }
411 }
412 MethodHandle handle = switch (methodName) {
413 case "equals" -> {
414 if (methodType != null && !methodType.equals(MethodType.methodType(boolean.class, recordClass, Object.class)))
415 throw new IllegalArgumentException("Bad method type: " + methodType);
416 yield makeEquals(recordClass, getterList);
417 }
418 case "hashCode" -> {
419 if (methodType != null && !methodType.equals(MethodType.methodType(int.class, recordClass)))
420 throw new IllegalArgumentException("Bad method type: " + methodType);
421 yield makeHashCode(recordClass, getterList);
422 }
423 case "toString" -> {
424 if (methodType != null && !methodType.equals(MethodType.methodType(String.class, recordClass)))
425 throw new IllegalArgumentException("Bad method type: " + methodType);
426 List<String> nameList = "".equals(names) ? List.of() : List.of(names.split(";"));
427 if (nameList.size() != getterList.size())
428 throw new IllegalArgumentException("Name list and accessor list do not match");
429 yield makeToString(lookup, recordClass, getters, nameList);
430 }
431 default -> throw new IllegalArgumentException(methodName);
|