< prev index next >

src/java.base/share/classes/java/lang/invoke/ValueBootstrapMethods.java

Print this page
rev 55127 : 8223351: [lworld] Primary mirror and nullable mirror for inline type
Reviewed-by: tbd


  90             System.out.println("generate BSM for " + lookup + "::" + name);
  91         }
  92         switch (name) {
  93             case "hashCode":
  94                 return hashCodeInvoker(lookup, name, methodType);
  95             case "equals":
  96                 return equalsInvoker(lookup, name, methodType);
  97             case "toString":
  98                 return toStringInvoker(lookup, name, methodType);
  99             default:
 100                 throw new IllegalArgumentException(name + " not valid method name");
 101         }
 102     }
 103 
 104     static class MethodHandleBuilder {
 105         static MethodHandle[] getters(Lookup lookup) {
 106             return getters(lookup, null);
 107         }
 108 
 109         static MethodHandle[] getters(Lookup lookup, Comparator<MethodHandle> comparator) {
 110             Class<?> type = lookup.lookupClass().asValueType();
 111             // filter static fields and synthetic fields
 112             Stream<MethodHandle> s = Arrays.stream(type.getDeclaredFields())
 113                 .filter(f -> !Modifier.isStatic(f.getModifiers()) && !f.isSynthetic())
 114                 .map(f -> {
 115                     try {
 116                         return lookup.unreflectGetter(f);
 117                     } catch (IllegalAccessException e) {
 118                         throw newLinkageError(e);
 119                     }
 120                 });
 121             if (comparator != null) {
 122                 s = s.sorted(comparator);
 123             }
 124             return s.toArray(MethodHandle[]::new);
 125         }
 126 
 127         static MethodHandle primitiveEquals(Class<?> primitiveType) {
 128             int index = Wrapper.forPrimitiveType(primitiveType).ordinal();
 129             return EQUALS[index];
 130         }


 156 
 157         /*
 158          * Produces a MethodHandle that returns boolean if two instances
 159          * of the given interface class are substitutable.
 160          *
 161          * Two interface values are i== iff
 162          * 1. if o1 and o2 are both reference objects then o1 r== o2; or
 163          * 2. if o1 and o2 are both values then o1 v== o2
 164          */
 165         static MethodHandle interfaceEquals(Class<?> type) {
 166             assert type.isInterface() || type == Object.class;
 167             MethodType mt = methodType(boolean.class, type, type);
 168             return guardWithTest(IS_SAME_VALUE_CLASS.asType(mt), VALUE_EQUALS.asType(mt), referenceEquals(type));
 169         }
 170 
 171         /*
 172          * Produces a MethodHandle that returns boolean if two value instances
 173          * of the given value class are substitutable.
 174          */
 175         static MethodHandle valueEquals(Class<?> c) {
 176             assert c.isValue();
 177             Class<?> type = c.asValueType();
 178             MethodType mt = methodType(boolean.class, type, type);
 179             MethodHandles.Lookup lookup = new MethodHandles.Lookup(type);
 180             MethodHandle[] getters = getters(lookup, TYPE_SORTER);
 181             MethodHandle instanceFalse = dropArguments(FALSE, 0, type, Object.class).asType(mt);
 182             MethodHandle accumulator = dropArguments(TRUE, 0, type, type);
 183             for (MethodHandle getter : getters) {
 184                 Class<?> ftype = getter.type().returnType();
 185                 MethodHandle eq = substitutableInvoker(ftype).asType(methodType(boolean.class, ftype, ftype));
 186                 MethodHandle thisFieldEqual = filterArguments(eq, 0, getter, getter);
 187                 accumulator = guardWithTest(thisFieldEqual, accumulator, instanceFalse);
 188             }
 189             // if both arguments are null, return true;
 190             // otherwise return accumulator;
 191             MethodHandle instanceTrue = dropArguments(TRUE, 0, type, Object.class).asType(mt);
 192             return guardWithTest(IS_NULL.asType(mt),
 193                                  instanceTrue.asType(mt),
 194                                  guardWithTest(IS_SAME_VALUE_CLASS.asType(mt),
 195                                                accumulator,
 196                                                instanceFalse));
 197         }


 200         private static boolean eq(byte a, byte b)       { return a == b; }
 201         private static boolean eq(short a, short b)     { return a == b; }
 202         private static boolean eq(char a, char b)       { return a == b; }
 203         private static boolean eq(int a, int b)         { return a == b; }
 204         private static boolean eq(long a, long b)       { return a == b; }
 205         private static boolean eq(float a, float b)     { return Float.compare(a, b) == 0; }
 206         private static boolean eq(double a, double b)   { return Double.compare(a, b) == 0; }
 207         private static boolean eq(boolean a, boolean b) { return a == b; }
 208         private static boolean eq(Object a, Object b)   { return a == b; }
 209 
 210         private static boolean isNull(Object a, Object b) {
 211             // avoid acmp that will call isSubstitutable
 212             if (a != null) return false;
 213             if (b != null) return false;
 214             return true;
 215         }
 216 
 217         private static boolean isSameValueClass(Object a, Object b) {
 218             if (a == null || b == null)
 219                 return false;
 220             return a.getClass().isValue() && a.getClass().asBoxType() == b.getClass().asBoxType();
 221         }
 222 
 223         private static boolean valueEq(Object a, Object b) {
 224             assert isSameValueClass(a, b);
 225             try {
 226                 Class<?> type = a.getClass();
 227                 return (boolean) valueEquals(type).invoke(type.cast(a), type.cast(b));
 228             } catch (Error|RuntimeException e) {
 229                 throw e;
 230             } catch (Throwable e) {
 231                 throw new InternalError(e);
 232             }
 233         }
 234 
 235         private static String toString(Object o) {
 236             return o != null ? o.toString() : "null";
 237         }
 238 
 239         private static MethodHandle toString(Class<?> type) {
 240             if (type.isArray()) {


 331             }
 332         }
 333 
 334         /**
 335          * A "salt" value used for this internal hashcode implementation that
 336          * needs to vary sufficiently from one run to the next so that
 337          * the default hashcode for value types will vary between JVM runs.
 338          */
 339         static final int SALT;
 340         static {
 341             long nt = System.nanoTime();
 342             int value = (int)((nt >>> 32) ^ nt);
 343             SALT = GetIntegerAction.privilegedGetProperty("value.bsm.salt", value);
 344         }
 345     }
 346 
 347     /*
 348      * Produces a method handle that computes the hashcode
 349      */
 350     private static MethodHandle hashCodeInvoker(Lookup lookup, String name, MethodType mt) {
 351         Class<?> type = lookup.lookupClass().asValueType();
 352         MethodHandle target = dropArguments(constant(int.class, SALT), 0, type);
 353         MethodHandle cls = dropArguments(constant(Class.class, type),0, type);
 354         MethodHandle classHashCode = filterReturnValue(cls, hashCodeForType(Class.class));
 355         MethodHandle combiner = filterArguments(HASH_COMBINER, 0, target, classHashCode);
 356         // int v = SALT * 31 + type.hashCode();
 357         MethodHandle init = permuteArguments(combiner, target.type(), 0, 0);
 358         MethodHandle[] getters = MethodHandleBuilder.getters(lookup);
 359         MethodHandle iterations = dropArguments(constant(int.class, getters.length), 0, type);
 360         MethodHandle[] hashers = new MethodHandle[getters.length];
 361         for (int i=0; i < getters.length; i++) {
 362             MethodHandle getter = getters[i];
 363             MethodHandle hasher = hashCodeForType(getter.type().returnType());
 364             hashers[i] = filterReturnValue(getter, hasher);
 365         }
 366 
 367         // for (int i=0; i < getters.length; i++) {
 368         //   v = computeHash(v, i, a);
 369         // }
 370         MethodHandle body = COMPUTE_HASH.bindTo(hashers)
 371                                         .asType(methodType(int.class, int.class, int.class, type));
 372         return countedLoop(iterations, init, body);
 373     }
 374 
 375     /*
 376      * Produces a method handle that invokes the toString method of a value object.
 377      */
 378     private static MethodHandle toStringInvoker(Lookup lookup, String name, MethodType mt) {
 379         Class<?> type = lookup.lookupClass().asValueType();
 380         MethodHandle[] getters = MethodHandleBuilder.getters(lookup);



 381         int length = getters.length;
 382         StringBuilder format = new StringBuilder();
 383         Class<?>[] parameterTypes = new Class<?>[length];
 384         // append the value class name
 385         format.append("[").append(type.getName());
 386         String separator = " ";
 387         for (int i = 0; i < length; i++) {
 388             MethodHandle getter = getters[i];
 389             MethodHandleInfo fieldInfo = lookup.revealDirect(getter);



 390             Class<?> ftype = fieldInfo.getMethodType().returnType();
 391             format.append(separator)
 392                   .append(fieldInfo.getName())
 393                   .append("=\1");
 394             getters[i]= filterReturnValue(getter, MethodHandleBuilder.toString(ftype));
 395             parameterTypes[i] = String.class;
 396         }
 397         format.append("]");
 398         try {
 399             MethodHandle target = StringConcatFactory.makeConcatWithConstants(lookup, "toString",
 400                     methodType(String.class, parameterTypes), format.toString()).dynamicInvoker();
 401             // apply getters
 402             target = filterArguments(target, 0, getters);
 403             // duplicate "this" argument from o::toString for each getter invocation
 404             target = permuteArguments(target, methodType(String.class, type), new int[length]);
 405             return target;
 406         } catch (StringConcatException e) {
 407             throw newLinkageError(e);
 408         }
 409     }
 410 
 411     /*
 412      * Produces a method handle that tests if two arguments are equals.
 413      */
 414     private static MethodHandle equalsInvoker(Lookup lookup, String name, MethodType mt) {
 415         Class<?> type = lookup.lookupClass().asValueType();
 416         // MethodHandle to compare all fields of two value objects
 417         MethodHandle[] getters = MethodHandleBuilder.getters(lookup, TYPE_SORTER);
 418         MethodHandle accumulator = dropArguments(TRUE, 0, type, type);
 419         MethodHandle instanceFalse = dropArguments(FALSE, 0, type, Object.class)
 420                                         .asType(methodType(boolean.class, type, type));
 421         for (MethodHandle getter : getters) {
 422             MethodHandle eq = equalsForType(getter.type().returnType());
 423             MethodHandle thisFieldEqual = filterArguments(eq, 0, getter, getter);
 424             accumulator = guardWithTest(thisFieldEqual, accumulator, instanceFalse);
 425         }
 426 
 427         // if o1 == o2 return true;
 428         // if (o1 and o2 are same value class) return accumulator;
 429         // return false;
 430         MethodHandle instanceTrue = dropArguments(TRUE, 0, type, Object.class).asType(mt);
 431         return guardWithTest(referenceEq().asType(mt),
 432                              instanceTrue.asType(mt),
 433                              guardWithTest(IS_SAME_VALUE_CLASS.asType(mt),
 434                                            accumulator.asType(mt),
 435                                            dropArguments(FALSE, 0, type, Object.class)));


 503      * @apiNote
 504      * This API is intended for performance evaluation of this idiom for
 505      * {@code acmp}.  Hence it is not in the {@link System} class.
 506      *
 507      * @param a an object
 508      * @param b an object to be compared with {@code a} for substitutability
 509      * @return {@code true} if the arguments are substitutable to each other;
 510      *         {@code false} otherwise.
 511      * @param <T> type
 512      * @see Float#equals(Object)
 513      * @see Double#equals(Object)
 514      */
 515     public static <T> boolean isSubstitutable(T a, Object b) {
 516         if (VERBOSE)
 517             System.out.println("substitutable " + a + " vs " + b);
 518         if (a == b) return true;
 519         if (a == null || b == null) return false;
 520         if (a.getClass() != b.getClass()) return false;
 521 
 522         try {
 523             Class<?> type = a.getClass().isValue() ? a.getClass().asValueType() : a.getClass();
 524             return (boolean) substitutableInvoker(type).invoke(a, b);
 525         } catch (Error|RuntimeException e) {
 526             throw e;
 527         } catch (Throwable e) {
 528             if (VERBOSE) e.printStackTrace();
 529             throw new InternalError(e);
 530         }
 531     }
 532 
 533     /**
 534      * Produces a method handle which tests if two arguments are
 535      * {@linkplain #isSubstitutable(Object, Object) substitutable}.
 536      *
 537      * <ul>
 538      * <li>If {@code T} is a non-floating point primitive type, this method
 539      *     returns a method handle testing the two arguments are the same value,
 540      *     i.e. {@code a == b}.
 541      * <li>If {@code T} is {@code float} or {@code double}, this method
 542      *     returns a method handle representing {@link Float#equals(Object)} or
 543      *     {@link Double#equals(Object)} respectively.


 549      *     for all fields {@code f} declared in {@code T}, where {@code U} is
 550      *     the type of {@code f}, if {@code a.f} and {@code b.f} are substitutable
 551      *     with respect to {@code U}.
 552      * <li>If {@code T} is an interface or {@code Object}, and
 553      *     {@code a} and {@code b} are of the same value class {@code V},
 554      *     this method returns a method handle that returns {@code true} if
 555      *     {@code a} and {@code b} are substitutable with respect to {@code V}.
 556      * </ul>
 557      *
 558      * @param type class type
 559      * @param <T> type
 560      * @return a method handle for substitutability test
 561      */
 562     static <T> MethodHandle substitutableInvoker(Class<T> type) {
 563         if (type.isPrimitive())
 564             return MethodHandleBuilder.primitiveEquals(type);
 565 
 566         if (type.isInterface() || type == Object.class)
 567             return MethodHandleBuilder.interfaceEquals(type);
 568 
 569         if (type.isValue())
 570             return SUBST_TEST_METHOD_HANDLES.get(type);
 571 
 572         return MethodHandleBuilder.referenceEquals(type);
 573     }
 574 
 575     // store the method handle for value types in ClassValue
 576     private static ClassValue<MethodHandle> SUBST_TEST_METHOD_HANDLES = new ClassValue<>() {
 577         @Override protected MethodHandle computeValue(Class<?> c) {
 578         return MethodHandleBuilder.valueEquals(c);
 579         }
 580     };
 581 
 582     private static final Comparator<MethodHandle> TYPE_SORTER = (mh1, mh2) -> {
 583         // sort the getters with the return type
 584         Class<?> t1 = mh1.type().returnType();
 585         Class<?> t2 = mh2.type().returnType();
 586         if (t1.isPrimitive()) {
 587             if (!t2.isPrimitive()) {
 588                 return 1;
 589             }


  90             System.out.println("generate BSM for " + lookup + "::" + name);
  91         }
  92         switch (name) {
  93             case "hashCode":
  94                 return hashCodeInvoker(lookup, name, methodType);
  95             case "equals":
  96                 return equalsInvoker(lookup, name, methodType);
  97             case "toString":
  98                 return toStringInvoker(lookup, name, methodType);
  99             default:
 100                 throw new IllegalArgumentException(name + " not valid method name");
 101         }
 102     }
 103 
 104     static class MethodHandleBuilder {
 105         static MethodHandle[] getters(Lookup lookup) {
 106             return getters(lookup, null);
 107         }
 108 
 109         static MethodHandle[] getters(Lookup lookup, Comparator<MethodHandle> comparator) {
 110             Class<?> type = lookup.lookupClass().asPrimaryType();
 111             // filter static fields and synthetic fields
 112             Stream<MethodHandle> s = Arrays.stream(type.getDeclaredFields())
 113                 .filter(f -> !Modifier.isStatic(f.getModifiers()) && !f.isSynthetic())
 114                 .map(f -> {
 115                     try {
 116                         return lookup.unreflectGetter(f);
 117                     } catch (IllegalAccessException e) {
 118                         throw newLinkageError(e);
 119                     }
 120                 });
 121             if (comparator != null) {
 122                 s = s.sorted(comparator);
 123             }
 124             return s.toArray(MethodHandle[]::new);
 125         }
 126 
 127         static MethodHandle primitiveEquals(Class<?> primitiveType) {
 128             int index = Wrapper.forPrimitiveType(primitiveType).ordinal();
 129             return EQUALS[index];
 130         }


 156 
 157         /*
 158          * Produces a MethodHandle that returns boolean if two instances
 159          * of the given interface class are substitutable.
 160          *
 161          * Two interface values are i== iff
 162          * 1. if o1 and o2 are both reference objects then o1 r== o2; or
 163          * 2. if o1 and o2 are both values then o1 v== o2
 164          */
 165         static MethodHandle interfaceEquals(Class<?> type) {
 166             assert type.isInterface() || type == Object.class;
 167             MethodType mt = methodType(boolean.class, type, type);
 168             return guardWithTest(IS_SAME_VALUE_CLASS.asType(mt), VALUE_EQUALS.asType(mt), referenceEquals(type));
 169         }
 170 
 171         /*
 172          * Produces a MethodHandle that returns boolean if two value instances
 173          * of the given value class are substitutable.
 174          */
 175         static MethodHandle valueEquals(Class<?> c) {
 176             assert c.isInlineClass();
 177             Class<?> type = c.asPrimaryType();
 178             MethodType mt = methodType(boolean.class, type, type);
 179             MethodHandles.Lookup lookup = new MethodHandles.Lookup(type);
 180             MethodHandle[] getters = getters(lookup, TYPE_SORTER);
 181             MethodHandle instanceFalse = dropArguments(FALSE, 0, type, Object.class).asType(mt);
 182             MethodHandle accumulator = dropArguments(TRUE, 0, type, type);
 183             for (MethodHandle getter : getters) {
 184                 Class<?> ftype = getter.type().returnType();
 185                 MethodHandle eq = substitutableInvoker(ftype).asType(methodType(boolean.class, ftype, ftype));
 186                 MethodHandle thisFieldEqual = filterArguments(eq, 0, getter, getter);
 187                 accumulator = guardWithTest(thisFieldEqual, accumulator, instanceFalse);
 188             }
 189             // if both arguments are null, return true;
 190             // otherwise return accumulator;
 191             MethodHandle instanceTrue = dropArguments(TRUE, 0, type, Object.class).asType(mt);
 192             return guardWithTest(IS_NULL.asType(mt),
 193                                  instanceTrue.asType(mt),
 194                                  guardWithTest(IS_SAME_VALUE_CLASS.asType(mt),
 195                                                accumulator,
 196                                                instanceFalse));
 197         }


 200         private static boolean eq(byte a, byte b)       { return a == b; }
 201         private static boolean eq(short a, short b)     { return a == b; }
 202         private static boolean eq(char a, char b)       { return a == b; }
 203         private static boolean eq(int a, int b)         { return a == b; }
 204         private static boolean eq(long a, long b)       { return a == b; }
 205         private static boolean eq(float a, float b)     { return Float.compare(a, b) == 0; }
 206         private static boolean eq(double a, double b)   { return Double.compare(a, b) == 0; }
 207         private static boolean eq(boolean a, boolean b) { return a == b; }
 208         private static boolean eq(Object a, Object b)   { return a == b; }
 209 
 210         private static boolean isNull(Object a, Object b) {
 211             // avoid acmp that will call isSubstitutable
 212             if (a != null) return false;
 213             if (b != null) return false;
 214             return true;
 215         }
 216 
 217         private static boolean isSameValueClass(Object a, Object b) {
 218             if (a == null || b == null)
 219                 return false;
 220             return a.getClass().isInlineClass() && a.getClass().asNullableType() == b.getClass().asNullableType();
 221         }
 222 
 223         private static boolean valueEq(Object a, Object b) {
 224             assert isSameValueClass(a, b);
 225             try {
 226                 Class<?> type = a.getClass();
 227                 return (boolean) valueEquals(type).invoke(type.cast(a), type.cast(b));
 228             } catch (Error|RuntimeException e) {
 229                 throw e;
 230             } catch (Throwable e) {
 231                 throw new InternalError(e);
 232             }
 233         }
 234 
 235         private static String toString(Object o) {
 236             return o != null ? o.toString() : "null";
 237         }
 238 
 239         private static MethodHandle toString(Class<?> type) {
 240             if (type.isArray()) {


 331             }
 332         }
 333 
 334         /**
 335          * A "salt" value used for this internal hashcode implementation that
 336          * needs to vary sufficiently from one run to the next so that
 337          * the default hashcode for value types will vary between JVM runs.
 338          */
 339         static final int SALT;
 340         static {
 341             long nt = System.nanoTime();
 342             int value = (int)((nt >>> 32) ^ nt);
 343             SALT = GetIntegerAction.privilegedGetProperty("value.bsm.salt", value);
 344         }
 345     }
 346 
 347     /*
 348      * Produces a method handle that computes the hashcode
 349      */
 350     private static MethodHandle hashCodeInvoker(Lookup lookup, String name, MethodType mt) {
 351         Class<?> type = lookup.lookupClass().asPrimaryType();
 352         MethodHandle target = dropArguments(constant(int.class, SALT), 0, type);
 353         MethodHandle cls = dropArguments(constant(Class.class, type),0, type);
 354         MethodHandle classHashCode = filterReturnValue(cls, hashCodeForType(Class.class));
 355         MethodHandle combiner = filterArguments(HASH_COMBINER, 0, target, classHashCode);
 356         // int v = SALT * 31 + type.hashCode();
 357         MethodHandle init = permuteArguments(combiner, target.type(), 0, 0);
 358         MethodHandle[] getters = MethodHandleBuilder.getters(lookup);
 359         MethodHandle iterations = dropArguments(constant(int.class, getters.length), 0, type);
 360         MethodHandle[] hashers = new MethodHandle[getters.length];
 361         for (int i=0; i < getters.length; i++) {
 362             MethodHandle getter = getters[i];
 363             MethodHandle hasher = hashCodeForType(getter.type().returnType());
 364             hashers[i] = filterReturnValue(getter, hasher);
 365         }
 366 
 367         // for (int i=0; i < getters.length; i++) {
 368         //   v = computeHash(v, i, a);
 369         // }
 370         MethodHandle body = COMPUTE_HASH.bindTo(hashers)
 371                                         .asType(methodType(int.class, int.class, int.class, type));
 372         return countedLoop(iterations, init, body);
 373     }
 374 
 375     /*
 376      * Produces a method handle that invokes the toString method of a value object.
 377      */
 378     private static MethodHandle toStringInvoker(Lookup lookup, String name, MethodType mt) {
 379         Class<?> type = lookup.lookupClass().asPrimaryType();
 380         MethodHandle[] getters = MethodHandleBuilder.getters(lookup);
 381         if (VERBOSE) {
 382             System.out.println("getter: " + Arrays.toString(getters));
 383         }
 384         int length = getters.length;
 385         StringBuilder format = new StringBuilder();
 386         Class<?>[] parameterTypes = new Class<?>[length];
 387         // append the value class name
 388         format.append("[").append(type.getName());
 389         String separator = " ";
 390         for (int i = 0; i < length; i++) {
 391             MethodHandle getter = getters[i];
 392             MethodHandleInfo fieldInfo = lookup.revealDirect(getter);
 393             if (VERBOSE) {
 394                 System.out.println("getter: " + getter + " field info: " + fieldInfo);
 395             }
 396             Class<?> ftype = fieldInfo.getMethodType().returnType();
 397             format.append(separator)
 398                   .append(fieldInfo.getName())
 399                   .append("=\1");
 400             getters[i]= filterReturnValue(getter, MethodHandleBuilder.toString(ftype));
 401             parameterTypes[i] = String.class;
 402         }
 403         format.append("]");
 404         try {
 405             MethodHandle target = StringConcatFactory.makeConcatWithConstants(lookup, "toString",
 406                     methodType(String.class, parameterTypes), format.toString()).dynamicInvoker();
 407             // apply getters
 408             target = filterArguments(target, 0, getters);
 409             // duplicate "this" argument from o::toString for each getter invocation
 410             target = permuteArguments(target, methodType(String.class, type), new int[length]);
 411             return target;
 412         } catch (StringConcatException e) {
 413             throw newLinkageError(e);
 414         }
 415     }
 416 
 417     /*
 418      * Produces a method handle that tests if two arguments are equals.
 419      */
 420     private static MethodHandle equalsInvoker(Lookup lookup, String name, MethodType mt) {
 421         Class<?> type = lookup.lookupClass().asPrimaryType();
 422         // MethodHandle to compare all fields of two value objects
 423         MethodHandle[] getters = MethodHandleBuilder.getters(lookup, TYPE_SORTER);
 424         MethodHandle accumulator = dropArguments(TRUE, 0, type, type);
 425         MethodHandle instanceFalse = dropArguments(FALSE, 0, type, Object.class)
 426                                         .asType(methodType(boolean.class, type, type));
 427         for (MethodHandle getter : getters) {
 428             MethodHandle eq = equalsForType(getter.type().returnType());
 429             MethodHandle thisFieldEqual = filterArguments(eq, 0, getter, getter);
 430             accumulator = guardWithTest(thisFieldEqual, accumulator, instanceFalse);
 431         }
 432 
 433         // if o1 == o2 return true;
 434         // if (o1 and o2 are same value class) return accumulator;
 435         // return false;
 436         MethodHandle instanceTrue = dropArguments(TRUE, 0, type, Object.class).asType(mt);
 437         return guardWithTest(referenceEq().asType(mt),
 438                              instanceTrue.asType(mt),
 439                              guardWithTest(IS_SAME_VALUE_CLASS.asType(mt),
 440                                            accumulator.asType(mt),
 441                                            dropArguments(FALSE, 0, type, Object.class)));


 509      * @apiNote
 510      * This API is intended for performance evaluation of this idiom for
 511      * {@code acmp}.  Hence it is not in the {@link System} class.
 512      *
 513      * @param a an object
 514      * @param b an object to be compared with {@code a} for substitutability
 515      * @return {@code true} if the arguments are substitutable to each other;
 516      *         {@code false} otherwise.
 517      * @param <T> type
 518      * @see Float#equals(Object)
 519      * @see Double#equals(Object)
 520      */
 521     public static <T> boolean isSubstitutable(T a, Object b) {
 522         if (VERBOSE)
 523             System.out.println("substitutable " + a + " vs " + b);
 524         if (a == b) return true;
 525         if (a == null || b == null) return false;
 526         if (a.getClass() != b.getClass()) return false;
 527 
 528         try {
 529             Class<?> type = a.getClass().isInlineClass() ? a.getClass().asPrimaryType() : a.getClass();
 530             return (boolean) substitutableInvoker(type).invoke(a, b);
 531         } catch (Error|RuntimeException e) {
 532             throw e;
 533         } catch (Throwable e) {
 534             if (VERBOSE) e.printStackTrace();
 535             throw new InternalError(e);
 536         }
 537     }
 538 
 539     /**
 540      * Produces a method handle which tests if two arguments are
 541      * {@linkplain #isSubstitutable(Object, Object) substitutable}.
 542      *
 543      * <ul>
 544      * <li>If {@code T} is a non-floating point primitive type, this method
 545      *     returns a method handle testing the two arguments are the same value,
 546      *     i.e. {@code a == b}.
 547      * <li>If {@code T} is {@code float} or {@code double}, this method
 548      *     returns a method handle representing {@link Float#equals(Object)} or
 549      *     {@link Double#equals(Object)} respectively.


 555      *     for all fields {@code f} declared in {@code T}, where {@code U} is
 556      *     the type of {@code f}, if {@code a.f} and {@code b.f} are substitutable
 557      *     with respect to {@code U}.
 558      * <li>If {@code T} is an interface or {@code Object}, and
 559      *     {@code a} and {@code b} are of the same value class {@code V},
 560      *     this method returns a method handle that returns {@code true} if
 561      *     {@code a} and {@code b} are substitutable with respect to {@code V}.
 562      * </ul>
 563      *
 564      * @param type class type
 565      * @param <T> type
 566      * @return a method handle for substitutability test
 567      */
 568     static <T> MethodHandle substitutableInvoker(Class<T> type) {
 569         if (type.isPrimitive())
 570             return MethodHandleBuilder.primitiveEquals(type);
 571 
 572         if (type.isInterface() || type == Object.class)
 573             return MethodHandleBuilder.interfaceEquals(type);
 574 
 575         if (type.isInlineClass())
 576             return SUBST_TEST_METHOD_HANDLES.get(type);
 577 
 578         return MethodHandleBuilder.referenceEquals(type);
 579     }
 580 
 581     // store the method handle for value types in ClassValue
 582     private static ClassValue<MethodHandle> SUBST_TEST_METHOD_HANDLES = new ClassValue<>() {
 583         @Override protected MethodHandle computeValue(Class<?> c) {
 584         return MethodHandleBuilder.valueEquals(c);
 585         }
 586     };
 587 
 588     private static final Comparator<MethodHandle> TYPE_SORTER = (mh1, mh2) -> {
 589         // sort the getters with the return type
 590         Class<?> t1 = mh1.type().returnType();
 591         Class<?> t2 = mh2.type().returnType();
 592         if (t1.isPrimitive()) {
 593             if (!t2.isPrimitive()) {
 594                 return 1;
 595             }
< prev index next >