1 /*
   2  * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   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 java.lang.invoke;
  27 
  28 import sun.invoke.util.Wrapper;
  29 import sun.security.action.GetIntegerAction;
  30 import sun.security.action.GetPropertyAction;
  31 
  32 import java.lang.reflect.Modifier;
  33 import java.util.Arrays;
  34 import java.util.Comparator;
  35 import java.util.Objects;
  36 import java.util.stream.Stream;
  37 
  38 import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
  39 import static java.lang.invoke.MethodHandles.*;
  40 import static java.lang.invoke.MethodType.*;
  41 import static java.lang.invoke.ValueBootstrapMethods.MethodHandleBuilder.*;
  42 
  43 
  44 /**
  45  * Bootstrap methods for value types
  46  */
  47 public final class ValueBootstrapMethods {
  48     private ValueBootstrapMethods() {}
  49     private static final boolean VERBOSE =
  50         GetPropertyAction.privilegedGetProperty("value.bsm.debug") != null;
  51 
  52     /**
  53      * Makes a bootstrap method for the named operation for the given Class.
  54      *
  55      * @apiNote {@code c} parameter for testing purpose.  This method will be removed.
  56      *
  57      * @param caller    A lookup context
  58      * @param name      The name of the method to implement.
  59      * @param type      The expected signature of the {@code CallSite}
  60      * @param c         Class
  61      * @return a CallSite whose target can be used to perform the named operation
  62      */
  63     public static CallSite makeBootstrapMethod(MethodHandles.Lookup caller,
  64                                                String name,
  65                                                MethodType type,
  66                                                Class<?> c) {
  67         MethodHandles.Lookup lookup = caller;
  68         if (caller.lookupClass() != c) {
  69             lookup = new MethodHandles.Lookup(c);
  70         }
  71         return makeBootstrapMethod(lookup, name, type);
  72     }
  73 
  74     /**
  75      * Makes a bootstrap method for the named operation for the given Class.
  76      *
  77      * @param lookup    A lookup context
  78      * @param name      The name of the method to implement.
  79      * @param type      The expected signature of the {@code CallSite}
  80      * @return a CallSite whose target can be used to perform the named operation
  81      */
  82     public static CallSite makeBootstrapMethod(MethodHandles.Lookup lookup,
  83                                                String name,
  84                                                MethodType type) {
  85         return new ConstantCallSite(generateTarget(lookup, name, type));
  86     }
  87 
  88     private static MethodHandle generateTarget(Lookup lookup, String name, MethodType methodType) {
  89         if (VERBOSE) {
  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         }
 131 
 132         static MethodHandle referenceEquals(Class<?> type) {
 133             return EQUALS[Wrapper.OBJECT.ordinal()].asType(methodType(boolean.class, type, type));
 134         }
 135 
 136         static MethodHandle referenceEq() {
 137             return EQUALS[Wrapper.OBJECT.ordinal()];
 138         }
 139 
 140         static MethodHandle hashCodeForType(Class<?> type) {
 141             if (type.isPrimitive()) {
 142                 int index = Wrapper.forPrimitiveType(type).ordinal();
 143                 return HASHCODE[index];
 144             } else {
 145                 return HASHCODE[Wrapper.OBJECT.ordinal()].asType(methodType(int.class, type));
 146             }
 147         }
 148 
 149         static MethodHandle equalsForType(Class<?> type) {
 150             if (type.isPrimitive()) {
 151                 return primitiveEquals(type);
 152             } else {
 153                 return OBJECTS_EQUALS.asType(methodType(boolean.class, type, type));
 154             }
 155         }
 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         }
 198 
 199         // ------ utility methods ------
 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()) {
 241                 Class<?> componentType = type.getComponentType();
 242                 if (componentType.isPrimitive()) {
 243                     int index = Wrapper.forPrimitiveType(componentType).ordinal();
 244                     return ARRAYS_TO_STRING[index];
 245                 } else {
 246                     return ARRAYS_TO_STRING[Wrapper.OBJECT.ordinal()].asType(methodType(String.class, type));
 247                 }
 248             } else {
 249                 return TO_STRING.asType(methodType(String.class, type));
 250             }
 251         }
 252 
 253         private static int hashCombiner(int accumulator, int value) {
 254             return accumulator * 31 + value;
 255         }
 256 
 257         private static int computeHashCode(MethodHandle[] hashers, int v, int i, Object o) {
 258             try {
 259                 int hc = (int)hashers[i].invoke(o);
 260                 return hashCombiner(v, hc);
 261             } catch (Error|RuntimeException e) {
 262                 throw e;
 263             } catch (Throwable e) {
 264                 throw new InternalError(e);
 265             }
 266         }
 267 
 268         private static final MethodHandle[] EQUALS = initEquals();
 269         private static final MethodHandle[] ARRAYS_TO_STRING = initArraysToString();
 270         private static final MethodHandle[] HASHCODE = initHashCode();
 271 
 272         static final MethodHandle IS_SAME_VALUE_CLASS =
 273             findStatic("isSameValueClass", methodType(boolean.class, Object.class, Object.class));
 274         static final MethodHandle VALUE_EQUALS =
 275              findStatic("valueEq", methodType(boolean.class, Object.class, Object.class));
 276         static final MethodHandle IS_NULL =
 277             findStatic("isNull", methodType(boolean.class, Object.class, Object.class));
 278         static final MethodHandle TO_STRING =
 279             findStatic("toString", methodType(String.class, Object.class));
 280         static final MethodHandle OBJECTS_EQUALS =
 281             findStatic(Objects.class, "equals", methodType(boolean.class, Object.class, Object.class));
 282 
 283         static final MethodHandle FALSE = constant(boolean.class, false);
 284         static final MethodHandle TRUE = constant(boolean.class, true);
 285         static final MethodHandle HASH_COMBINER =
 286             findStatic("hashCombiner", methodType(int.class, int.class, int.class));
 287         static final MethodHandle COMPUTE_HASH =
 288             findStatic("computeHashCode", methodType(int.class, MethodHandle[].class, int.class, int.class, Object.class));
 289 
 290         private static MethodHandle[] initEquals() {
 291             MethodHandle[] mhs = new MethodHandle[Wrapper.COUNT];
 292             for (Wrapper wrapper : Wrapper.values()) {
 293                 if (wrapper == Wrapper.VOID) continue;
 294 
 295                 Class<?> type = wrapper.primitiveType();
 296                 mhs[wrapper.ordinal()] = findStatic("eq", methodType(boolean.class, type, type));
 297             }
 298             return mhs;
 299         }
 300 
 301         private static MethodHandle[] initArraysToString() {
 302             MethodHandle[] mhs = new MethodHandle[Wrapper.COUNT];
 303             for (Wrapper wrapper : Wrapper.values()) {
 304                 if (wrapper == Wrapper.VOID) continue;
 305 
 306                 Class<?> arrayType = wrapper.arrayType();
 307                 mhs[wrapper.ordinal()] = findStatic(Arrays.class, "toString", methodType(String.class, arrayType));
 308             }
 309             return mhs;
 310         }
 311 
 312         private static MethodHandle[] initHashCode() {
 313             MethodHandle[] mhs = new MethodHandle[Wrapper.COUNT];
 314             for (Wrapper wrapper : Wrapper.values()) {
 315                 if (wrapper == Wrapper.VOID) continue;
 316                 Class<?> cls = wrapper == Wrapper.OBJECT ? Objects.class : wrapper.wrapperType();
 317                 mhs[wrapper.ordinal()] = findStatic(cls, "hashCode",
 318                                                     methodType(int.class, wrapper.primitiveType()));
 319             }
 320             return mhs;
 321         }
 322 
 323         private static MethodHandle findStatic(String name, MethodType methodType) {
 324             return findStatic(MethodHandleBuilder.class, name, methodType);
 325         }
 326         private static MethodHandle findStatic(Class<?> cls, String name, MethodType methodType) {
 327             try {
 328                 return IMPL_LOOKUP.findStatic(cls, name, methodType);
 329             } catch (NoSuchMethodException|IllegalAccessException e) {
 330                 throw newLinkageError(e);
 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)));
 436     }
 437 
 438     private static LinkageError newLinkageError(Throwable e) {
 439         return (LinkageError) new LinkageError().initCause(e);
 440     }
 441 
 442     /**
 443      * Returns {@code true} if the arguments are <em>substitutable</em> to each
 444      * other and {@code false} otherwise.
 445      * <em>Substitutability</em> means that they cannot be distinguished from
 446      * each other in any data-dependent way, meaning that it is safe to substitute
 447      * one for the other.
 448      *
 449      * <ul>
 450      * <li>If {@code a} and {@code b} are both {@code null}, this method returns
 451      *     {@code true}.
 452      * <li>If {@code a} and {@code b} are both value instances of the same class
 453      *     {@code V}, this method returns {@code true} if, for all fields {@code f}
 454      *      declared in {@code V}, {@code a.f} and {@code b.f} are substitutable.
 455      * <li>If {@code a} and {@code b} are both primitives of the same type,
 456      *     this method returns {@code a == b} with the following exception:
 457      *     <ul>
 458      *     <li> If {@code a} and {@code b} both represent {@code NaN},
 459      *          this method returns {@code true}, even though {@code NaN == NaN}
 460      *          has the value {@code false}.
 461      *     <li> If {@code a} is floating point positive zero while {@code b} is
 462      *          floating point negative zero, or vice versa, this method
 463      *          returns {@code false}, even though {@code +0.0 == -0.0} has
 464      *          the value {@code true}.
 465      *     </ul>
 466      * <li>If {@code a} and {@code b} are both instances of the same reference type,
 467      *     this method returns {@code a == b}.
 468      * <li>Otherwise this method returns {@code false}.
 469      * </ul>
 470      *
 471      * <p>For example,
 472      * <pre>{@code interface Number { ... }
 473      * // ordinary reference class
 474      * class IntNumber implements Number { ... }
 475      * // value class
 476      * value class IntValue implements Number {
 477      *     int i;
 478      *     :
 479      *     public static IntValue of(int i) {...}     // IntValue::of creates a new value instance
 480      * }
 481      * // value class with an Object field
 482      * value class RefValue {
 483      *     Object o;
 484      *     :
 485      * }
 486      *
 487      * var val1 = IntValue.of(10);
 488      * var val2 = IntValue.of(10);                    // val1 and val2 have the same value
 489      * var ref1 = new IntNumber(10);                  // ref1 and ref2 are two reference instances
 490      * var ref2 = new IntNumber(10);
 491      * assertTrue(isSubstitutable(val1, val2));       // val1 and val2 are both value instances of IntValue
 492      * assertFalse(isSubstitutable(ref1, ref2));      // ref1 and ref2 are two reference instances that are not substitutable
 493      * assertTrue(isSubstitutable(ref1, ref1));       // a reference instance is substitutable with itself
 494      *
 495      * var rval1 = RefValue.of(List.of("list"));      // rval1.o and rval2.o both contain a list of one-single element "list"
 496      * var rval2 = RefValue.of(List.of("list");
 497      * var rval3 = RefValue.of(rval1.o);
 498      *
 499      * assertFalse(isSubstitutable(rval1, rval2));    // rval1.o and rval2.o are two different List instances and hence not substitutable
 500      * assertTrue(isSubstitutable(rval1, rval3 ));    // rval1.o and rval3.o are the same reference instance
 501      * }</pre>
 502      *
 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.
 544      * <li>If {@code T} is a reference type that is not {@code Object} and not an
 545      *     interface, this method returns a method handle testing
 546      *     the two arguments are the same reference, i.e. {@code a == b}.
 547      * <li>If {@code T} is a value type, this method returns
 548      *     a method handle that returns {@code true} if
 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             }
 590         } else {
 591             if (t2.isPrimitive()) {
 592                 return -1;
 593             }
 594         }
 595         return -1;
 596     };
 597 }