59 public final class ObjectMethods {
60
61 private ObjectMethods() { }
62
63 private static final int MAX_STRING_CONCAT_SLOTS = 20;
64
65 private static final MethodHandle FALSE = MethodHandles.zero(boolean.class);
66 private static final MethodHandle TRUE = MethodHandles.constant(boolean.class, true);
67 private static final MethodHandle ZERO = MethodHandles.zero(int.class);
68 private static final MethodHandle CLASS_IS_INSTANCE;
69 private static final MethodHandle IS_NULL;
70 private static final MethodHandle IS_ARG0_NULL;
71 private static final MethodHandle IS_ARG1_NULL;
72 private static final MethodHandle OBJECT_EQ;
73 private static final MethodHandle HASH_COMBINER;
74 private static final MethodType MT_OBJECT_BOOLEAN = MethodType.methodType(boolean.class, Object.class);
75 private static final MethodType MT_INT = MethodType.methodType(int.class);
76 private static final MethodTypeDesc MTD_OBJECT_BOOLEAN = MethodTypeDesc.of(CD_boolean, CD_Object);
77 private static final MethodTypeDesc MTD_INT = MethodTypeDesc.of(CD_int);
78
79 private static final HashMap<Class<?>, MethodHandle> primitiveEquals = new HashMap<>();
80 private static final HashMap<Class<?>, MethodHandle> primitiveHashers = new HashMap<>();
81
82 static {
83 try {
84 Class<ObjectMethods> OBJECT_METHODS_CLASS = ObjectMethods.class;
85 MethodHandles.Lookup publicLookup = MethodHandles.publicLookup();
86 MethodHandles.Lookup lookup = MethodHandles.lookup();
87
88 CLASS_IS_INSTANCE = publicLookup.findVirtual(Class.class, "isInstance",
89 MethodType.methodType(boolean.class, Object.class));
90
91 var objectsIsNull = publicLookup.findStatic(Objects.class, "isNull",
92 MethodType.methodType(boolean.class, Object.class));
93 IS_NULL = objectsIsNull;
94 IS_ARG0_NULL = MethodHandles.dropArguments(objectsIsNull, 1, Object.class);
95 IS_ARG1_NULL = MethodHandles.dropArguments(objectsIsNull, 0, Object.class);
96
97 OBJECT_EQ = lookup.findStatic(OBJECT_METHODS_CLASS, "eq",
98 MethodType.methodType(boolean.class, Object.class, Object.class));
99 HASH_COMBINER = lookup.findStatic(OBJECT_METHODS_CLASS, "hashCombiner",
502 * @return a call site if invoked by indy, or a method handle
503 * if invoked by a condy
504 * @throws IllegalArgumentException if the bootstrap arguments are invalid
505 * or inconsistent
506 * @throws NullPointerException if any argument is {@code null} or if any element
507 * in the {@code getters} array is {@code null}
508 * @throws Throwable if any exception is thrown during call site construction
509 */
510 public static Object bootstrap(MethodHandles.Lookup lookup, String methodName, TypeDescriptor type,
511 Class<?> recordClass,
512 String names,
513 MethodHandle... getters) throws Throwable {
514 requireNonNull(lookup);
515 requireNonNull(methodName);
516 requireNonNull(type);
517 requireNonNull(recordClass);
518 requireNonNull(names);
519 requireNonNull(getters);
520 Arrays.stream(getters).forEach(Objects::requireNonNull);
521 MethodType methodType;
522 if (type instanceof MethodType mt)
523 methodType = mt;
524 else {
525 methodType = null;
526 if (!MethodHandle.class.equals(type))
527 throw new IllegalArgumentException(type.toString());
528 }
529 List<MethodHandle> getterList = List.of(getters);
530 MethodHandle handle = switch (methodName) {
531 case "equals" -> {
532 if (methodType != null && !methodType.equals(MethodType.methodType(boolean.class, recordClass, Object.class)))
533 throw new IllegalArgumentException("Bad method type: " + methodType);
534 yield makeEquals(lookup, recordClass, getterList);
535 }
536 case "hashCode" -> {
537 if (methodType != null && !methodType.equals(MethodType.methodType(int.class, recordClass)))
538 throw new IllegalArgumentException("Bad method type: " + methodType);
539 yield makeHashCode(lookup, recordClass, getterList);
540 }
541 case "toString" -> {
542 if (methodType != null && !methodType.equals(MethodType.methodType(String.class, recordClass)))
543 throw new IllegalArgumentException("Bad method type: " + methodType);
544 List<String> nameList = "".equals(names) ? List.of() : List.of(names.split(";"));
545 if (nameList.size() != getterList.size())
546 throw new IllegalArgumentException("Name list and accessor list do not match");
547 yield makeToString(lookup, recordClass, getters, nameList);
548 }
549 default -> throw new IllegalArgumentException(methodName);
|
59 public final class ObjectMethods {
60
61 private ObjectMethods() { }
62
63 private static final int MAX_STRING_CONCAT_SLOTS = 20;
64
65 private static final MethodHandle FALSE = MethodHandles.zero(boolean.class);
66 private static final MethodHandle TRUE = MethodHandles.constant(boolean.class, true);
67 private static final MethodHandle ZERO = MethodHandles.zero(int.class);
68 private static final MethodHandle CLASS_IS_INSTANCE;
69 private static final MethodHandle IS_NULL;
70 private static final MethodHandle IS_ARG0_NULL;
71 private static final MethodHandle IS_ARG1_NULL;
72 private static final MethodHandle OBJECT_EQ;
73 private static final MethodHandle HASH_COMBINER;
74 private static final MethodType MT_OBJECT_BOOLEAN = MethodType.methodType(boolean.class, Object.class);
75 private static final MethodType MT_INT = MethodType.methodType(int.class);
76 private static final MethodTypeDesc MTD_OBJECT_BOOLEAN = MethodTypeDesc.of(CD_boolean, CD_Object);
77 private static final MethodTypeDesc MTD_INT = MethodTypeDesc.of(CD_int);
78
79 /* package-private */
80 static final HashMap<Class<?>, MethodHandle> primitiveEquals = new HashMap<>();
81
82 private static final HashMap<Class<?>, MethodHandle> primitiveHashers = new HashMap<>();
83
84 static {
85 try {
86 Class<ObjectMethods> OBJECT_METHODS_CLASS = ObjectMethods.class;
87 MethodHandles.Lookup publicLookup = MethodHandles.publicLookup();
88 MethodHandles.Lookup lookup = MethodHandles.lookup();
89
90 CLASS_IS_INSTANCE = publicLookup.findVirtual(Class.class, "isInstance",
91 MethodType.methodType(boolean.class, Object.class));
92
93 var objectsIsNull = publicLookup.findStatic(Objects.class, "isNull",
94 MethodType.methodType(boolean.class, Object.class));
95 IS_NULL = objectsIsNull;
96 IS_ARG0_NULL = MethodHandles.dropArguments(objectsIsNull, 1, Object.class);
97 IS_ARG1_NULL = MethodHandles.dropArguments(objectsIsNull, 0, Object.class);
98
99 OBJECT_EQ = lookup.findStatic(OBJECT_METHODS_CLASS, "eq",
100 MethodType.methodType(boolean.class, Object.class, Object.class));
101 HASH_COMBINER = lookup.findStatic(OBJECT_METHODS_CLASS, "hashCombiner",
504 * @return a call site if invoked by indy, or a method handle
505 * if invoked by a condy
506 * @throws IllegalArgumentException if the bootstrap arguments are invalid
507 * or inconsistent
508 * @throws NullPointerException if any argument is {@code null} or if any element
509 * in the {@code getters} array is {@code null}
510 * @throws Throwable if any exception is thrown during call site construction
511 */
512 public static Object bootstrap(MethodHandles.Lookup lookup, String methodName, TypeDescriptor type,
513 Class<?> recordClass,
514 String names,
515 MethodHandle... getters) throws Throwable {
516 requireNonNull(lookup);
517 requireNonNull(methodName);
518 requireNonNull(type);
519 requireNonNull(recordClass);
520 requireNonNull(names);
521 requireNonNull(getters);
522 Arrays.stream(getters).forEach(Objects::requireNonNull);
523 MethodType methodType;
524 if (type instanceof MethodType mt) {
525 methodType = mt;
526 if (mt.parameterType(0) != recordClass) {
527 throw new IllegalArgumentException("Bad method type: " + mt);
528 }
529 } else {
530 methodType = null;
531 if (!MethodHandle.class.equals(type))
532 throw new IllegalArgumentException(type.toString());
533 }
534 List<MethodHandle> getterList = List.of(getters);
535 for (MethodHandle getter : getterList) {
536 if (getter.type().parameterType(0) != recordClass) {
537 throw new IllegalArgumentException("Bad receiver type: " + getter);
538 }
539 }
540 MethodHandle handle = switch (methodName) {
541 case "equals" -> {
542 if (methodType != null && !methodType.equals(MethodType.methodType(boolean.class, recordClass, Object.class)))
543 throw new IllegalArgumentException("Bad method type: " + methodType);
544 yield makeEquals(lookup, recordClass, getterList);
545 }
546 case "hashCode" -> {
547 if (methodType != null && !methodType.equals(MethodType.methodType(int.class, recordClass)))
548 throw new IllegalArgumentException("Bad method type: " + methodType);
549 yield makeHashCode(lookup, recordClass, getterList);
550 }
551 case "toString" -> {
552 if (methodType != null && !methodType.equals(MethodType.methodType(String.class, recordClass)))
553 throw new IllegalArgumentException("Bad method type: " + methodType);
554 List<String> nameList = "".equals(names) ? List.of() : List.of(names.split(";"));
555 if (nameList.size() != getterList.size())
556 throw new IllegalArgumentException("Name list and accessor list do not match");
557 yield makeToString(lookup, recordClass, getters, nameList);
558 }
559 default -> throw new IllegalArgumentException(methodName);
|