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().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 } 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.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 } 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().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()) { 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().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))); 442 } 443 444 private static LinkageError newLinkageError(Throwable e) { 445 return (LinkageError) new LinkageError().initCause(e); 446 } 447 448 /** 449 * Returns {@code true} if the arguments are <em>substitutable</em> to each 450 * other and {@code false} otherwise. 451 * <em>Substitutability</em> means that they cannot be distinguished from 452 * each other in any data-dependent way, meaning that it is safe to substitute 453 * one for the other. 454 * 455 * <ul> 456 * <li>If {@code a} and {@code b} are both {@code null}, this method returns 457 * {@code true}. 458 * <li>If {@code a} and {@code b} are both value instances of the same class 459 * {@code V}, this method returns {@code true} if, for all fields {@code f} 460 * declared in {@code V}, {@code a.f} and {@code b.f} are substitutable. 461 * <li>If {@code a} and {@code b} are both primitives of the same type, 462 * this method returns {@code a == b} with the following exception: 463 * <ul> 464 * <li> If {@code a} and {@code b} both represent {@code NaN}, 465 * this method returns {@code true}, even though {@code NaN == NaN} 466 * has the value {@code false}. 467 * <li> If {@code a} is floating point positive zero while {@code b} is 468 * floating point negative zero, or vice versa, this method 469 * returns {@code false}, even though {@code +0.0 == -0.0} has 470 * the value {@code true}. 471 * </ul> 472 * <li>If {@code a} and {@code b} are both instances of the same reference type, 473 * this method returns {@code a == b}. 474 * <li>Otherwise this method returns {@code false}. 475 * </ul> 476 * 477 * <p>For example, 478 * <pre>{@code interface Number { ... } 479 * // ordinary reference class 480 * class IntNumber implements Number { ... } 481 * // value class 482 * value class IntValue implements Number { 483 * int i; 484 * : 485 * public static IntValue of(int i) {...} // IntValue::of creates a new value instance 486 * } 487 * // value class with an Object field 488 * value class RefValue { 489 * Object o; 490 * : 491 * } 492 * 493 * var val1 = IntValue.of(10); 494 * var val2 = IntValue.of(10); // val1 and val2 have the same value 495 * var ref1 = new IntNumber(10); // ref1 and ref2 are two reference instances 496 * var ref2 = new IntNumber(10); 497 * assertTrue(isSubstitutable(val1, val2)); // val1 and val2 are both value instances of IntValue 498 * assertFalse(isSubstitutable(ref1, ref2)); // ref1 and ref2 are two reference instances that are not substitutable 499 * assertTrue(isSubstitutable(ref1, ref1)); // a reference instance is substitutable with itself 500 * 501 * var rval1 = RefValue.of(List.of("list")); // rval1.o and rval2.o both contain a list of one-single element "list" 502 * var rval2 = RefValue.of(List.of("list"); 503 * var rval3 = RefValue.of(rval1.o); 504 * 505 * assertFalse(isSubstitutable(rval1, rval2)); // rval1.o and rval2.o are two different List instances and hence not substitutable 506 * assertTrue(isSubstitutable(rval1, rval3 )); // rval1.o and rval3.o are the same reference instance 507 * }</pre> 508 * 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. 550 * <li>If {@code T} is a reference type that is not {@code Object} and not an 551 * interface, this method returns a method handle testing 552 * the two arguments are the same reference, i.e. {@code a == b}. 553 * <li>If {@code T} is a value type, this method returns 554 * a method handle that returns {@code true} if 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 } 596 } else { 597 if (t2.isPrimitive()) { 598 return -1; 599 } 600 } 601 return -1; 602 }; 603 }