1 /* 2 * Copyright (c) 2018, 2025, 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.runtime; 27 28 import java.lang.invoke.MethodHandle; 29 import java.lang.invoke.MethodHandles; 30 import java.lang.invoke.MethodType; 31 import java.lang.invoke.VarHandle; 32 import java.util.ArrayDeque; 33 import java.util.ArrayList; 34 import java.util.Comparator; 35 import java.util.Deque; 36 import java.util.HashMap; 37 import java.util.HashSet; 38 import java.util.Iterator; 39 import java.util.LinkedHashMap; 40 import java.util.LinkedList; 41 import java.util.List; 42 import java.util.Map; 43 import java.util.Objects; 44 import java.util.Set; 45 import java.util.concurrent.ConcurrentHashMap; 46 import java.util.function.Function; 47 import java.util.function.Predicate; 48 import java.util.stream.Stream; 49 50 import jdk.internal.access.JavaLangInvokeAccess; 51 import jdk.internal.access.SharedSecrets; 52 import jdk.internal.value.LayoutIteration; 53 import jdk.internal.value.ValueClass; 54 import sun.invoke.util.Wrapper; 55 56 import static java.lang.invoke.MethodHandles.constant; 57 import static java.lang.invoke.MethodHandles.dropArguments; 58 import static java.lang.invoke.MethodHandles.filterArguments; 59 import static java.lang.invoke.MethodHandles.foldArguments; 60 import static java.lang.invoke.MethodHandles.guardWithTest; 61 import static java.lang.invoke.MethodHandles.permuteArguments; 62 import static java.lang.invoke.MethodType.methodType; 63 import static java.lang.runtime.ObjectMethods.primitiveEquals; 64 import static java.util.stream.Collectors.joining; 65 import static java.util.stream.Collectors.toList; 66 import static java.util.stream.Collectors.toMap; 67 68 /** 69 * Implementation for Object::equals and Object::hashCode for value objects. 70 * 71 * ValueObjectMethods::isSubstitutable and valueObjectHashCode are 72 * private entry points called by VM. 73 */ 74 final class ValueObjectMethods { 75 private ValueObjectMethods() {} 76 private static final boolean VERBOSE = 77 System.getProperty("value.bsm.debug") != null; 78 private static final int MAX_NODE_VISITS = 79 Integer.getInteger("jdk.value.recursion.threshold", Integer.MAX_VALUE); 80 private static final JavaLangInvokeAccess JLIA = SharedSecrets.getJavaLangInvokeAccess(); 81 82 static class MethodHandleBuilder { 83 private static final HashMap<Class<?>, MethodHandle> primitiveSubstitutable = new HashMap<>(); 84 85 static { 86 primitiveSubstitutable.putAll(primitiveEquals); // adopt all the primitive eq methods 87 primitiveSubstitutable.put(float.class, 88 findStatic("eqValue", methodType(boolean.class, float.class, float.class))); 89 primitiveSubstitutable.put(double.class, 90 findStatic("eqValue", methodType(boolean.class, double.class, double.class))); 91 } 92 93 static Stream<MethodHandle> getterStream(Class<?> type, Comparator<MethodHandle> comparator) { 94 // filter static fields 95 List<MethodHandle> mhs = LayoutIteration.ELEMENTS.get(type); 96 if (comparator != null) { 97 mhs = new ArrayList<>(mhs); 98 mhs.sort(comparator); 99 } 100 return mhs.stream(); 101 } 102 103 static MethodHandle hashCodeForType(Class<?> type) { 104 if (type.isPrimitive()) { 105 int index = Wrapper.forPrimitiveType(type).ordinal(); 106 return HASHCODE[index]; 107 } else { 108 return HASHCODE[Wrapper.OBJECT.ordinal()].asType(methodType(int.class, type)); 109 } 110 } 111 112 static MethodHandle builtinPrimitiveSubstitutable(Class<?> type) { 113 return primitiveSubstitutable.get(type); 114 } 115 116 /* 117 * Produces a MethodHandle that returns boolean if two instances 118 * of the given reference type are substitutable. 119 * 120 * Two values of reference type are substitutable i== iff 121 * 1. if o1 and o2 are both reference objects then o1 r== o2; or 122 * 2. if o1 and o2 are both values then o1 v== o2 123 * 124 * At invocation time, it needs a dynamic check on the objects and 125 * do the substitutability test if they are of a value class. 126 */ 127 static MethodHandle referenceTypeEquals(Class<?> type) { 128 return OBJECT_EQUALS.asType(methodType(boolean.class, type, type)); 129 } 130 131 static Class<?> fieldType(MethodHandle getter) { 132 Class<?> ftype = getter.type().returnType(); 133 return ftype; 134 } 135 136 private static List<Class<?>> valueTypeFields(Class<?> type) { 137 return LayoutIteration.ELEMENTS.get(type).stream() 138 .<Class<?>>map(mh -> mh.type().returnType()) 139 .filter(ValueClass::isConcreteValueClass) 140 .distinct() 141 .toList(); 142 } 143 144 /* 145 * Produces a MethodHandle that returns boolean if two value objects 146 * are substitutable. The method type is (V, V)boolean. 147 */ 148 static MethodHandle valueTypeEquals(Class<?> type) { 149 var builder = METHOD_HANDLE_BUILDERS.get(type); 150 if (builder == null) { 151 builder = newBuilder(type); 152 } 153 return builder.equalsTarget(); 154 } 155 156 /* 157 * Produces a MethodHandle that computes the hash code of a value object. 158 * The method type of the return MethodHandle is (V)int. 159 */ 160 static MethodHandle valueTypeHashCode(Class<?> type) { 161 var builder = METHOD_HANDLE_BUILDERS.get(type); 162 if (builder == null) { 163 builder = newBuilder(type); 164 } 165 return builder.hashCodeTarget(); 166 } 167 168 /* 169 * Produces a MethodHandle that returns boolean if the given non-recursively typed 170 * fields of the two value objects are substitutable. The method type is (V, V)boolean 171 */ 172 static MethodHandle valueTypeEquals(Class<?> type, List<MethodHandle> getters) { 173 assert ValueClass.isConcreteValueClass(type); 174 175 MethodType mt = methodType(boolean.class, type, type); 176 MethodHandle instanceTrue = dropArguments(TRUE, 0, type, Object.class).asType(mt); 177 MethodHandle instanceFalse = dropArguments(FALSE, 0, type, Object.class).asType(mt); 178 MethodHandle accumulator = dropArguments(TRUE, 0, type, type); 179 for (MethodHandle getter : getters) { 180 Class<?> ftype = fieldType(getter); 181 var eq = substitutableInvoker(ftype).asType(methodType(boolean.class, ftype, ftype)); 182 var thisFieldEqual = filterArguments(eq, 0, getter, getter); 183 accumulator = guardWithTest(thisFieldEqual, accumulator, instanceFalse); 184 } 185 186 // if both arguments are null, return true; 187 // otherwise return accumulator; 188 return guardWithTest(IS_NULL.asType(mt), 189 instanceTrue, 190 guardWithTest(IS_SAME_VALUE_CLASS.asType(mt), 191 accumulator, 192 instanceFalse)); 193 } 194 195 /* 196 * Produces a MethodHandle that returns the hash code computed from 197 * the given non-recursively-typed fields of a value object. 198 * The method type is (V)int. 199 */ 200 static MethodHandle valueTypeHashCode(Class<?> type, List<MethodHandle> getters) { 201 assert ValueClass.isConcreteValueClass(type); 202 203 MethodHandle target = dropArguments(constant(int.class, SALT), 0, type); 204 MethodHandle classHasher = dropArguments(hashCodeForType(Class.class).bindTo(type), 0, type); 205 MethodHandle hashCombiner = dropArguments(HASH_COMBINER, 2, type); 206 MethodHandle accumulator = foldArguments(foldArguments(hashCombiner, 1, classHasher), 0, target); 207 for (MethodHandle getter : getters) { 208 Class<?> ft = fieldType(getter); 209 // For primitive types or reference types, this calls Objects::hashCode. 210 // For value objects and the hashCode method is not overridden, 211 // VM will call valueObjectHashCode to compute the hash code. 212 var hasher = hashCodeForType(ft); 213 var hashThisField = filterArguments(hasher, 0, getter); // (R)I 214 var combineHashes = foldArguments(hashCombiner, 1, hashThisField); 215 accumulator = foldArguments(combineHashes, 0, accumulator); 216 } 217 return accumulator; 218 } 219 220 // ------ utility methods ------ 221 private static boolean eq(Object a, Object b) { 222 if (a == null && b == null) return true; 223 if (a == null || b == null) return false; 224 if (a.getClass() != b.getClass()) return false; 225 return a.getClass().isValue() ? valueEquals(a, b) : (a == b); 226 } 227 228 /* 229 * Returns true if two value objects are substitutable. 230 */ 231 private static boolean valueEquals(Object a, Object b) { 232 assert a != null && b != null && isSameValueClass(a, b); 233 try { 234 Class<?> type = a.getClass(); 235 return (boolean) substitutableInvoker(type).invoke(type.cast(a), type.cast(b)); 236 } catch (Error|RuntimeException e) { 237 throw e; 238 } catch (Throwable e) { 239 throw new InternalError(e); 240 } 241 } 242 243 private static boolean isNull(Object a, Object b) { 244 // avoid acmp that will call isSubstitutable 245 if (a != null) return false; 246 if (b != null) return false; 247 return true; 248 } 249 250 /* 251 * Returns true if the given objects are of the same value class. 252 * 253 * Two objects are of the same value class iff: 254 * 1. a != null and b != null 255 * 2. the declaring class of a and b is the same value class 256 */ 257 private static boolean isSameValueClass(Object a, Object b) { 258 if (a == null || b == null) return false; 259 260 return a.getClass().isValue() && a.getClass() == b.getClass(); 261 } 262 263 private static int hashCombiner(int accumulator, int value) { 264 return accumulator * 31 + value; 265 } 266 267 private static int[] newCounter(int[] counter) { 268 return new int[] { counter[0] }; 269 } 270 271 private static boolean recurValueEq(MethodHandle target, Object o1, Object o2, int[] counter) { 272 assert counter[0] > 0; 273 274 if (o1 == null && o2 == null) return true; 275 if (o1 == null || o2 == null) return false; 276 if (o1.getClass() != o2.getClass()) return false; 277 278 if (--counter[0] == 0) { 279 throw new StackOverflowError("fail to evaluate == for value class " + o1.getClass().getName()); 280 } 281 282 try { 283 return (boolean) target.invoke(o1, o2, counter); 284 } catch (Error|RuntimeException e) { 285 throw e; 286 } catch (Throwable e) { 287 throw new InternalError(e); 288 } 289 } 290 291 private static int recurValueHashCode(MethodHandle target, Object o, int[] counter) { 292 assert counter[0] > 0; 293 294 if (o == null) return 0; 295 296 if (--counter[0] == 0) { 297 throw new StackOverflowError("fail to evaluate hashCode for value class " + o.getClass().getName()); 298 } 299 300 try { 301 return (int) target.invoke(o, counter); 302 } catch (Error|RuntimeException e) { 303 throw e; 304 } catch (Throwable e) { 305 throw new InternalError(e); 306 } 307 } 308 309 private static final MethodHandle FALSE = constant(boolean.class, false); 310 private static final MethodHandle TRUE = constant(boolean.class, true); 311 // Substitutability test for float 312 private static boolean eqValue(float a, float b) { 313 return Float.floatToRawIntBits(a) == Float.floatToRawIntBits(b); 314 } 315 // Substitutability test for double 316 private static boolean eqValue(double a, double b) { 317 return Double.doubleToRawLongBits(a) == Double.doubleToRawLongBits(b); 318 } 319 private static final MethodHandle OBJECT_EQUALS = 320 findStatic("eq", methodType(boolean.class, Object.class, Object.class)); 321 private static final MethodHandle IS_SAME_VALUE_CLASS = 322 findStatic("isSameValueClass", methodType(boolean.class, Object.class, Object.class)); 323 private static final MethodHandle IS_NULL = 324 findStatic("isNull", methodType(boolean.class, Object.class, Object.class)); 325 private static final MethodHandle HASH_COMBINER = 326 findStatic("hashCombiner", methodType(int.class, int.class, int.class)); 327 private static final MethodHandle[] HASHCODE = initHashCode(); 328 private static final MethodHandle RECUR_VALUE_EQ = 329 findStatic("recurValueEq", methodType(boolean.class, MethodHandle.class, Object.class, Object.class, int[].class)); 330 private static final MethodHandle RECUR_VALUE_HASHCODE = 331 findStatic("recurValueHashCode", methodType(int.class, MethodHandle.class, Object.class, int[].class)); 332 private static final MethodHandle NEW_COUNTER = 333 findStatic("newCounter", methodType(int[].class, int[].class)); 334 335 private static MethodHandle[] initHashCode() { 336 MethodHandle[] mhs = new MethodHandle[Wrapper.COUNT]; 337 for (Wrapper wrapper : Wrapper.values()) { 338 if (wrapper == Wrapper.VOID) continue; 339 Class<?> cls = wrapper == Wrapper.OBJECT ? Objects.class : wrapper.wrapperType(); 340 mhs[wrapper.ordinal()] = findStatic(cls, "hashCode", 341 methodType(int.class, wrapper.primitiveType())); 342 } 343 return mhs; 344 } 345 346 private static MethodHandle findStatic(String name, MethodType methodType) { 347 return findStatic(MethodHandleBuilder.class, name, methodType); 348 } 349 private static MethodHandle findStatic(Class<?> cls, String name, MethodType methodType) { 350 try { 351 return JLIA.findStatic(cls, name, methodType); 352 } catch (IllegalAccessException e) { 353 throw newLinkageError(e); 354 } 355 } 356 357 /** 358 * A "salt" value used for this internal hashcode implementation that 359 * needs to vary sufficiently from one run to the next so that 360 * the default hashcode for value classes will vary between JVM runs. 361 */ 362 static final int SALT; 363 static { 364 long nt = System.nanoTime(); 365 int value = (int)((nt >>> 32) ^ nt); 366 SALT = Integer.getInteger("value.bsm.salt", value); 367 } 368 369 static MethodHandleBuilder newBuilder(Class<?> type) { 370 assert ValueClass.isConcreteValueClass(type); 371 372 Deque<Class<?>> deque = new ArrayDeque<>(); 373 deque.add(type); 374 Map<Class<?>, MethodHandleBuilder> visited = new HashMap<>(); 375 var builder = new MethodHandleBuilder(type, deque, visited); 376 visited.put(type, builder); 377 return builder; 378 } 379 380 enum Status { 381 NOT_START, 382 IN_PROGRESS, 383 TRAVERSAL_DONE, 384 READY 385 } 386 387 final Class<?> type; 388 final List<Class<?>> fieldValueTypes; 389 // a map of the type of a field T to a cycle of T -> ... -> V 390 // where V is this builder's value type 391 final Map<Class<?>, List<Class<?>>> cyclicMembers = new HashMap<>(); 392 // recursively-typed fields declared in this builder's value type 393 final Set<Class<?>> recurFieldTypes = new HashSet<>(); 394 final Deque<Class<?>> path; 395 final Map<Class<?>, MethodHandleBuilder> visited;; 396 volatile Status status = Status.NOT_START; 397 volatile MethodHandle equalsTarget; 398 volatile MethodHandle hashCodeTarget; 399 400 static final VarHandle STATUS_HANDLE; 401 static { 402 VarHandle vh = null; 403 try { 404 vh = MethodHandles.lookup().findVarHandle(MethodHandleBuilder.class, "status", Status.class); 405 } catch (ReflectiveOperationException e) { 406 throw new InternalError(e); 407 } 408 STATUS_HANDLE = vh; 409 } 410 411 /** 412 * Constructs a new MethodHandleBuilder for the given value type. 413 * 414 * @param type a value class 415 * @param path the graph traversal 416 * @param visited a map of a visited type to a builder 417 */ 418 private MethodHandleBuilder(Class<?> type, Deque<Class<?>> path, Map<Class<?>, MethodHandleBuilder> visited) { 419 assert ValueClass.isConcreteValueClass(type) : type; 420 this.type = type; 421 this.fieldValueTypes = valueTypeFields(type); 422 this.path = path; 423 this.visited = visited; 424 if (VERBOSE) { 425 System.out.println("New builder for " + type.getName() + " " + path); 426 } 427 } 428 429 /* 430 * Returns a method handle that implements equals method for this builder's value class. 431 */ 432 MethodHandle equalsTarget() { 433 if (status != Status.READY) 434 throw new IllegalStateException(type.getName() + " not ready"); 435 436 var mh = equalsTarget; 437 if (mh != null) return mh; 438 439 generateMethodHandle(); 440 return equalsTarget; 441 } 442 443 /* 444 * Returns a method handle that implements hashCode method for this builder's value class. 445 */ 446 MethodHandle hashCodeTarget() { 447 if (status != Status.READY) 448 throw new IllegalStateException(type.getName() + " not ready"); 449 450 var mh = hashCodeTarget; 451 if (mh != null) return mh; 452 453 generateMethodHandle(); 454 return hashCodeTarget; 455 } 456 457 /* 458 * Build the graph for this builder's value type. Detect all cycles. 459 * This builder after this method returns is in DONE_TRAVERSAL or READY status. 460 * 461 * A builder for type V will change to READY status when the entire graph for V 462 * is traversed (i.e. all builders in this graph are in DONE_TRAVERSAL or READY 463 * status). 464 */ 465 MethodHandleBuilder build() { 466 if (status == Status.READY) return this; 467 468 if (!STATUS_HANDLE.compareAndSet(this, Status.NOT_START, Status.IN_PROGRESS)) { 469 throw new RuntimeException(type.getName() + " in progress"); 470 } 471 472 // traverse the graph and find all cycles 473 detectCycles(); 474 475 if (!STATUS_HANDLE.compareAndSet(this, Status.IN_PROGRESS, Status.TRAVERSAL_DONE)) { 476 throw new RuntimeException(type.getName() + " failed to set done traversal. " + status); 477 } 478 479 // Check if this node V is ready for building equals/hashCode method handles. 480 // V is ready if the types of all its fields are done traversal. 481 if (ready()) { 482 // Do a pass on all the cycles containing V. V is ready. 483 // If a node N in the cycle has completed the traversal (i.e. cycles are detected), 484 // call ready() on N to update its status if ready. 485 for (List<Class<?>> cycle : cyclicMembers.values()) { 486 cycle.stream().filter(c -> c != type) 487 .map(visited::get) 488 .filter(b -> b.status == Status.TRAVERSAL_DONE) 489 .forEach(MethodHandleBuilder::ready); 490 } 491 } 492 return this; 493 } 494 495 /* 496 * Traverses the graph and finds all cycles. 497 */ 498 private void detectCycles() { 499 LinkedList<Class<?>> deque = new LinkedList<>(); 500 deque.addAll(fieldValueTypes); 501 while (!deque.isEmpty()) { 502 Class<?> n = deque.pop(); 503 // detect cyclic membership 504 if (path.contains(n)) { 505 List<Class<?>> cycle = new ArrayList<>(); 506 Iterator<Class<?>> iter = path.iterator(); 507 while (iter.hasNext()) { 508 Class<?> c = iter.next(); 509 cycle.add(c); 510 if (c == n) break; 511 } 512 cyclicMembers.put(n, cycle); 513 path.pop(); 514 continue; 515 } 516 517 try { 518 path.push(n); 519 if (!visited.containsKey(n)) { 520 // Duplicate the path and pass it to an unvisited node 521 Deque<Class<?>> newPath = new ArrayDeque<>(); 522 newPath.addAll(path); 523 visited.computeIfAbsent(n, c -> new MethodHandleBuilder(n, newPath, visited)); 524 } 525 526 var builder = visited.get(n); 527 switch (builder.status) { 528 case NOT_START -> builder.build(); 529 case TRAVERSAL_DONE -> builder.ready(); 530 } 531 } finally { 532 path.pop(); 533 } 534 } 535 536 // propagate the cycles to the recursively-typed value classes 537 // For each cycle A -> B -> C -> A, the cycle is recorded in all 538 // the nodes (A, B, and C) in this cycle. 539 for (Map.Entry<Class<?>, List<Class<?>>> e : cyclicMembers.entrySet()) { 540 Class<?> c = e.getKey(); 541 List<Class<?>> cycle = e.getValue(); 542 var builder = visited.get(c); 543 for (Class<?> ft : cycle) { 544 if (ft != c && builder.fieldValueTypes.contains(ft)) { 545 var v = builder.cyclicMembers.put(ft, cycle); 546 assert v == null || cycle.equals(v) : "mismatched cycle: " + v + " vs " + cycle; 547 } 548 } 549 } 550 } 551 552 /* 553 * Tests if this builder is ready for generating equals and hashCode 554 * method handles for the value class. 555 * 556 * This builder is ready if and only if the type graph of all its fields 557 * are traversed and all cycles are detected. 558 * 559 * Before setting to READY, the recursively-typed fields are recorded 560 * that includes all types in the cycles and the field types which 561 * references recursive types 562 */ 563 private boolean ready() { 564 if (status == Status.READY) return true; 565 566 boolean inProgress = fieldValueTypes.stream().map(visited::get) 567 .anyMatch(b -> b.status == Status.IN_PROGRESS); 568 if (inProgress) 569 return false; 570 571 // collect the recursively-typed value classes required by this method handle 572 // all types in the cycles and the field types which references recursive types 573 recurFieldTypes.addAll(cyclicMembers.keySet()); 574 for (Class<?> c : fieldValueTypes) { 575 if (c == type) continue; 576 577 // if this field type references a recursive type 578 var b = visited.get(c); 579 if (b.cyclicMembers.size() > 0 || b.recurFieldTypes.size() > 0) 580 recurFieldTypes.add(c); 581 }; 582 583 // Finished recording recursively-typed fields. Set to READY. 584 if (!STATUS_HANDLE.compareAndSet(this, Status.TRAVERSAL_DONE, Status.READY)) { 585 throw new RuntimeException(type.getName() + " failed to set READY. " + status); 586 } 587 return true; 588 } 589 590 void generateMethodHandle() { 591 if (status != Status.READY) 592 throw new IllegalStateException(type.getName() + " not ready"); 593 594 // non-recursive value type 595 if (recurFieldTypes.isEmpty()) { 596 if (cyclicMembers.size() > 0) 597 throw new RuntimeException(type.getName() + " should not reach here"); 598 599 this.equalsTarget = valueTypeEquals(type, getterStream(type, TYPE_SORTER).toList()); 600 this.hashCodeTarget = valueTypeHashCode(type, getterStream(type, null).toList()); 601 return; 602 } 603 604 if (VERBOSE) { 605 System.out.println(debugString()); 606 } 607 608 // generate the base function for each recursive type 609 // boolean base1(MethodHandle entry, MethodHandle base1, MethodHandle base2,....., Object o1, Object o2, int[] counter) 610 // : 611 // boolean baseN(MethodHandle entry, MethodHandle base1, MethodHandle base2,....., Object o1, Object o2, int[] counter) 612 // 613 List<Class<?>> recursiveTypes = aggregateRecursiveTypes(); 614 Map<Class<?>, MethodHandle> bases = new LinkedHashMap<>(); 615 Map<Class<?>, MethodHandle> hashCodeBases = new LinkedHashMap<>(); 616 for (Class<?> c : recursiveTypes) { 617 bases.put(c, visited.get(c).generateSubstBase(recursiveTypes)); 618 hashCodeBases.put(c, visited.get(c).generateHashCodeBase(recursiveTypes)); 619 } 620 621 var handles = bases.values().stream().toArray(MethodHandle[]::new); 622 var hashCodeHandles = hashCodeBases.values().stream().toArray(MethodHandle[]::new); 623 624 // The entry point for equals for this value type T looks like this: 625 // 626 // boolean entry(MethodHandle entry, MethodHandle base1, MethodHandle base2,....., Object o1, Object o2, int[] counter) { 627 // int[] newCounter = new int[] { counter[0] } ; 628 // return baseT(o1, o2, newCounter); 629 // } 630 this.equalsTarget = newValueEquals(recursiveTypes, handles); 631 this.hashCodeTarget = newValueHashCode(recursiveTypes, hashCodeHandles); 632 633 // Precompute the method handles for all recursive data types in the cycles 634 // They share the generated base method handles. 635 var cycles = cyclicMembers.values().stream().flatMap(List::stream) 636 .filter(c -> c != type) 637 .collect(toMap(Function.identity(), visited::get)); 638 for (Class<?> n : cycles.keySet()) { 639 var builder = cycles.get(n); 640 if (builder.status != Status.READY) { 641 throw new InternalError(type.getName() + " is not ready: " + status); 642 } 643 644 var mh = builder.equalsTarget; 645 var mh2 = builder.hashCodeTarget; 646 if (mh != null && mh2 != null) { 647 continue; 648 } 649 650 // precompute the method handles for each recursive type in the cycle 651 if (mh == null) { 652 builder.equalsTarget = builder.newValueEquals(recursiveTypes, handles); 653 } 654 if (mh2 == null) { 655 builder.hashCodeTarget = builder.newValueHashCode(recursiveTypes, hashCodeHandles); 656 } 657 } 658 659 // cache the builders with precomputed method handles in the cache 660 synchronized (CACHED_METHOD_HANDLE_BUILDERS) { 661 for (Class<?> n : cycles.keySet()) { 662 try { 663 // the builder is added to the builder cache and propapate to 664 // the class value 665 CACHED_METHOD_HANDLE_BUILDERS.computeIfAbsent(n, cycles::get); 666 METHOD_HANDLE_BUILDERS.get(n); 667 } finally { 668 // Remove it from the builder cache once it's in class value 669 CACHED_METHOD_HANDLE_BUILDERS.remove(n); 670 } 671 } 672 } 673 674 // equals and hashCode are generated. Clear the path and visited builders. 675 clear(); 676 } 677 678 private void clear() { 679 path.clear(); 680 visited.clear(); 681 } 682 683 /* 684 * Aggregates all recursive data types for this builder's value types. 685 * The result is used in generating a recursive method handle 686 * for this builder's value type. 687 * 688 * A graph of V: 689 * V -> P -> V 690 * -> N -> N (self-recursive) 691 * -> E -> F -> E 692 * 693 * V, P, N, E, F are the mutual recursive types for V. The recursive method handle 694 * for V is created with the base functions for V, P, N, E, F and it can mutually 695 * call the recursive method handle for these types. Specifically, MH_V calls 696 * MH_P which calls MH_V, MH_N which calls itself, and MH_E which calls MH_F. 697 */ 698 private List<Class<?>> aggregateRecursiveTypes() { 699 boolean ready = true; 700 for (List<Class<?>> cycle : cyclicMembers.values()) { 701 // ensure all nodes in all cycles that are done traversal and ready for 702 // method handle generation 703 cycle.stream().filter(c -> c != type) 704 .map(visited::get) 705 .filter(b -> b.status == Status.TRAVERSAL_DONE) 706 .forEach(MethodHandleBuilder::ready); 707 708 // check the status 709 ready = ready && cycle.stream().filter(c -> c != type) 710 .map(visited::get) 711 .allMatch(b -> b.status == Status.READY); 712 } 713 714 if (!ready) { 715 throw new IllegalStateException(type.getName() + " " + status); 716 } 717 718 /* 719 * Traverse the graph for V to find all mutual recursive types for V. 720 * 721 * Node T is a mutual recursive type for V if any of the following: 722 * 1. T is a recursively-typed field in V 723 * 2. T is a type involved the cycles from V ... -> T ... -> V 724 * 3. T is a mutual recursive type for N where N is a mutual recursive type for V. 725 */ 726 Deque<Class<?>> deque = new ArrayDeque<>(); 727 List<Class<?>> recurTypes = new ArrayList<>(); 728 recurTypes.add(type); 729 Stream.concat(recurFieldTypes.stream(), 730 cyclicMembers.values().stream().flatMap(List::stream)) 731 .filter(Predicate.not(deque::contains)).forEach(deque::add); 732 while (!deque.isEmpty()) { 733 Class<?> c = deque.pop(); 734 if (recurTypes.contains(c)) continue; 735 736 recurTypes.add(c); 737 738 var builder = visited.get(c); 739 Stream.concat(builder.recurFieldTypes.stream(), 740 builder.cyclicMembers.values().stream().flatMap(List::stream)) 741 .filter(n -> !recurTypes.contains(n) && !deque.contains(n)) 742 .forEach(deque::push); 743 } 744 return recurTypes; 745 } 746 747 /* 748 * Create a new method handle that implements equals(T, Object) for value class T 749 * for this builder using the given base method handles. The return method handle 750 * is capable of recursively calling itself for value class T whose entry point: 751 * boolean entry(MethodHandle entry, MethodHandle base1, MethodHandle base2, ..., Object o1, Object o2, int[] counter) { 752 * int[] newCounter = new int[] { counter[0] }; 753 * return baseT(o1, o2, newCounter); 754 * } 755 * 756 * The counter is used to keep of node visits and throw StackOverflowError 757 * if the counter gets "unreasonably" large of a cyclic value graph 758 * (regardless of how many real stack frames were consumed.) 759 */ 760 MethodHandle newValueEquals(List<Class<?>> recursiveTypes, MethodHandle[] bases) { 761 var entry = equalsEntry(recursiveTypes); 762 var mh = MethodHandles.insertArguments(recursive(entry, bases), 2, new int[] {MAX_NODE_VISITS}); 763 return mh.asType(methodType(boolean.class, type, type)); 764 } 765 766 /* 767 * Create a new method handle that implements hashCode(T) for value class T 768 * for this builder using the given base method handles. The return method handle 769 * is capable of recursively calling itself for value class T whose entry point: 770 * boolean entry(MethodHandle entry, MethodHandle base1, MethodHandle base2, ..., Object o, int[] counter) { 771 * int[] newCounter = new int[] { counter[0] }; 772 * return baseT(o, newCounter); 773 * } 774 * 775 * The counter is used to keep of node visits and throw StackOverflowError 776 * if the counter gets "unreasonably" large of a cyclic value graph 777 * (regardless of how many real stack frames were consumed.) 778 */ 779 MethodHandle newValueHashCode(List<Class<?>> recursiveTypes, MethodHandle[] bases) { 780 var entry = hashCodeEntry(recursiveTypes); 781 var mh = MethodHandles.insertArguments(recursive(entry, bases), 1, new int[] {MAX_NODE_VISITS}); 782 return mh.asType(methodType(int.class, type)); 783 } 784 785 /* 786 * Create a method handle where the first N+1 parameters are MethodHandle and 787 * N is the number of the recursive value types and followed with 788 * Object, Object and int[] parameters. The pseudo code looks like this: 789 * 790 * boolean eq(MethodHandle entry, MethodHandle base1, MethodHandle base2, ..., Object o1, Object o2, int[] counter) { 791 * if (o1 == null && o2 == null) return true; 792 * if (o1 == null || o2 == null) return false; 793 * if (o1.getClass() != o2. getClass()) return false; 794 * 795 * int[] newCounter = new int[] { counter[0]; } 796 * return (boolean) baseT.invoke(o1, o2, newCounter); 797 * } 798 */ 799 MethodHandle equalsEntry(List<Class<?>> recursiveTypes) { 800 List<Class<?>> leadingMHParams = new ArrayList<>(); 801 // the first MethodHandle parameter is this entry point 802 // followed with MethodHandle parameter for each mutual exclusive value class 803 int mhParamCount = recursiveTypes.size()+1; 804 for (int i=0; i < mhParamCount; i++) { 805 leadingMHParams.add(MethodHandle.class); 806 } 807 808 MethodType mt = methodType(boolean.class, Stream.concat(leadingMHParams.stream(), Stream.of(type, type, int[].class)) 809 .collect(toList())); 810 var allParameters = mt.parameterList(); 811 MethodHandle instanceTrue = dropArguments(TRUE, 0, allParameters).asType(mt); 812 MethodHandle instanceFalse = dropArguments(FALSE, 0, allParameters).asType(mt); 813 MethodHandle isNull = dropArguments(dropArguments(IS_NULL, 0, leadingMHParams), mhParamCount+2, int[].class).asType(mt); 814 MethodHandle isSameValueType = dropArguments(dropArguments(IS_SAME_VALUE_CLASS, 0, leadingMHParams), mhParamCount+2, int[].class).asType(mt); 815 816 int index = recursiveTypes.indexOf(type); 817 var mtype = methodType(boolean.class, Stream.concat(leadingMHParams.stream(), Stream.of(type, type, int[].class)).collect(toList())); 818 var recurEq = RECUR_VALUE_EQ.asType(methodType(boolean.class, MethodHandle.class, type, type, int[].class)); 819 var eq = permuteArguments(recurEq, mtype, index+1, mhParamCount, mhParamCount+1, mhParamCount+2); 820 eq = filterArguments(eq, mhParamCount+2, NEW_COUNTER); 821 822 // if both arguments are null, return true; 823 // otherwise return the method handle corresponding to this type 824 return guardWithTest(isNull, 825 instanceTrue, 826 guardWithTest(isSameValueType, eq, instanceFalse)); 827 } 828 829 /* 830 * A base method for substitutability test for a recursive data type, 831 * a value class with cyclic membership. 832 * 833 * The signature of this base method is: 834 * boolean base(MethodHandle entry, MethodHandle base1, MethodHandle base2, ..., V o1, V o2, int[] counter) 835 * 836 * where the first N+1 parameters are MethodHandle and N is the number of 837 * the recursive value types and followed with Object, Object and int[] parameters. 838 * 839 * This method first calls the method handle that tests the substitutability 840 * of all fields that are not recursively-typed, if any, and then test 841 * the substitutability of the fields that are of each recursive value type. 842 * The pseudo code looks like this: 843 * 844 * boolean base(MethodHandle entry, MethodHandle base1, MethodHandle base2, ..., V o1, V o2, int[] counter) { 845 * if (o1 == null && o2 == null) return true; 846 * if (o1 == null || o2 == null) return false; 847 * if (o1.getClass() != o2. getClass()) return false; 848 * 849 * for each non-recursively-typed field { 850 * if (field value of o1 != field value of o2) return false; 851 * } 852 * 853 * for each recursively-typed field of type T { 854 * if (--counter[0] == 0) throw new StackOverflowError(); 855 * // baseT is the method handle corresponding to the recursive type T 856 * boolean rc = (boolean) baseT.invoke(o1, o2, counter); 857 * if (!rc) return false; 858 * } 859 * return true; 860 * } 861 */ 862 MethodHandle generateSubstBase(List<Class<?>> recursiveTypes) { 863 List<MethodHandle> nonRecurGetters = new ArrayList<>(); 864 Map<Class<?>, List<MethodHandle>> recurGetters = new LinkedHashMap<>(); 865 getterStream(type, TYPE_SORTER).forEach(mh -> { 866 Class<?> ft = fieldType(mh); 867 if (!this.recurFieldTypes.contains(ft)) { 868 nonRecurGetters.add(mh); 869 } else { 870 assert recursiveTypes.contains(ft); 871 recurGetters.computeIfAbsent(ft, t -> new ArrayList<>()).add(mh); 872 } 873 }); 874 875 // The first parameter is the method handle of the entry point 876 // followed with one MethodHandle for each recursive value type 877 List<Class<?>> leadingMHParams = new ArrayList<>(); 878 int mhParamCount = recursiveTypes.size()+1; 879 for (int i=0; i < mhParamCount; i++) { 880 leadingMHParams.add(MethodHandle.class); 881 } 882 883 MethodType mt = methodType(boolean.class, 884 Stream.concat(leadingMHParams.stream(), Stream.of(type, type, int[].class)).collect(toList())); 885 var allParameters = mt.parameterList(); 886 887 var instanceTrue = dropArguments(TRUE, 0, allParameters).asType(mt); 888 var instanceFalse = dropArguments(FALSE, 0, allParameters).asType(mt); 889 var accumulator = dropArguments(TRUE, 0, allParameters).asType(mt); 890 var isNull = dropArguments(dropArguments(IS_NULL, 0, leadingMHParams), mhParamCount+2, int[].class).asType(mt); 891 var isSameValueType = dropArguments(dropArguments(IS_SAME_VALUE_CLASS, 0, leadingMHParams), mhParamCount+2, int[].class).asType(mt); 892 893 // This value class contains cyclic membership. 894 // Create a method handle that first calls the method handle that tests 895 // the substitutability of all fields that are not recursively-typed, if any, 896 // and then test the substitutability of the fields that are of each recursive 897 // value type. 898 // 899 // Method handle for the substitutability test for recursive types is built 900 // before that for non-recursive types. 901 for (Map.Entry<Class<?>, List<MethodHandle>> e : recurGetters.entrySet()) { 902 Class<?> ft = e.getKey(); 903 int index = recursiveTypes.indexOf(ft); 904 var mtype = methodType(boolean.class, 905 Stream.concat(leadingMHParams.stream(), Stream.of(ft, ft, int[].class)).collect(toList())); 906 var recurEq = RECUR_VALUE_EQ.asType(methodType(boolean.class, MethodHandle.class, ft, ft, int[].class)); 907 var eq = permuteArguments(recurEq, mtype, index+1, mhParamCount, mhParamCount+1, mhParamCount+2); 908 for (MethodHandle getter : e.getValue()) { 909 assert ft == fieldType(getter); 910 var thisFieldEqual = filterArguments(eq, mhParamCount, getter, getter); 911 accumulator = guardWithTest(thisFieldEqual, accumulator, instanceFalse); 912 } 913 } 914 915 if (nonRecurGetters.isEmpty()) { 916 // if both arguments are null, return true; 917 // otherwise return accumulator; 918 return guardWithTest(isNull, 919 instanceTrue, 920 guardWithTest(isSameValueType, accumulator, instanceFalse)); 921 } else { 922 // method handle for substitutability test of the non-recursive-typed fields 923 var mh = valueTypeEquals(type, nonRecurGetters); 924 mh = dropArguments(dropArguments(mh, 0, leadingMHParams), mhParamCount+2, int[].class).asType(mt); 925 return guardWithTest(mh, accumulator, instanceFalse); 926 } 927 } 928 929 /* 930 * Create a method handle where the first N+1 parameters are MethodHandle and 931 * N is the number of the recursive value types and followed with 932 * Object and int[] parameters. The pseudo code looks like this: 933 * 934 * int hashCode(MethodHandle entry, MethodHandle base1, MethodHandle base2, ..., Object o, int[] counter) { 935 * int[] newCounter = new int[] { counter[0]; } 936 * return (int) baseT.invoke(o, newCounter); 937 * } 938 */ 939 MethodHandle hashCodeEntry(List<Class<?>> recursiveTypes) { 940 List<Class<?>> leadingMHParams = new ArrayList<>(); 941 int mhParamCount = recursiveTypes.size()+1; 942 // the first MethodHandle parameter is this entry point 943 // followed with MethodHandle parameter for each mutual exclusive value class 944 for (int i=0; i < mhParamCount; i++) { 945 leadingMHParams.add(MethodHandle.class); 946 } 947 948 int index = recursiveTypes.indexOf(type); 949 var mtype = methodType(int.class, Stream.concat(leadingMHParams.stream(), Stream.of(type, int[].class)).collect(toList())); 950 var recurHashCode = RECUR_VALUE_HASHCODE.asType(methodType(int.class, MethodHandle.class, type, int[].class)); 951 var mh = permuteArguments(recurHashCode, mtype, index+1, mhParamCount, mhParamCount+1); 952 return filterArguments(mh, mhParamCount+1, NEW_COUNTER); 953 } 954 955 /** 956 * A base method for computing the hashcode for a recursive data type, 957 * a value class with cyclic membership. 958 * 959 * The signature of this base method is: 960 * int base(MethodHandle entry, MethodHandle base1, MethodHandle base2, ..., V o, int[] counter) 961 * 962 * where the first N+1 parameters are MethodHandle and N is the number of 963 * the recursive value types and followed with Object and int[] parameters. 964 * 965 * This method will first invoke a method handle to compute the hash code 966 * of the not recursively-typed fields. Then compute the hash code of the 967 * remaining recursively-typed fields. 968 */ 969 MethodHandle generateHashCodeBase(List<Class<?>> recursiveTypes) { 970 assert status == Status.READY; 971 972 List<MethodHandle> nonRecurGetters = new ArrayList<>(); 973 Map<Class<?>, List<MethodHandle>> recurGetters = new LinkedHashMap<>(); 974 getterStream(type, null).forEach(mh -> { 975 Class<?> ft = fieldType(mh); 976 if (!this.recurFieldTypes.contains(ft)) { 977 nonRecurGetters.add(mh); 978 } else { 979 assert recursiveTypes.contains(ft); 980 recurGetters.computeIfAbsent(ft, t -> new ArrayList<>()).add(mh); 981 } 982 }); 983 984 int mhParamCount = recursiveTypes.size()+1; 985 List<Class<?>> leadingMHParams = new ArrayList<>(); 986 for (int i=0; i < mhParamCount; i++) { // include entry point 987 leadingMHParams.add(MethodHandle.class); 988 } 989 990 MethodType mt = methodType(int.class, 991 Stream.concat(leadingMHParams.stream(), Stream.of(type, int[].class)).collect(toList())); 992 var allParameters = mt.parameterList(); 993 var hashCombiner = dropArguments(HASH_COMBINER, 2, allParameters); 994 var salt = dropArguments(constant(int.class, SALT), 0, allParameters); 995 var classHasher = dropArguments(hashCodeForType(Class.class).bindTo(type), 0, allParameters); 996 var accumulator = foldArguments(foldArguments(hashCombiner, 1, classHasher), 0, salt); 997 for (MethodHandle getter : nonRecurGetters) { 998 Class<?> ft = fieldType(getter); 999 var hasher = dropArguments(hashCodeForType(ft), 0, leadingMHParams); 1000 var hashThisField = filterArguments(hasher, mhParamCount, getter); 1001 var combineHashes = foldArguments(hashCombiner, 1, hashThisField); 1002 accumulator = foldArguments(combineHashes, 0, accumulator); 1003 } 1004 1005 for (Map.Entry<Class<?>, List<MethodHandle>> e : recurGetters.entrySet()) { 1006 Class<?> ft = e.getKey(); 1007 int index = recursiveTypes.indexOf(ft); 1008 var mtype = methodType(int.class, Stream.concat(leadingMHParams.stream(), Stream.of(ft, int[].class)).collect(toList())); 1009 var recurHashCode = RECUR_VALUE_HASHCODE.asType(methodType(int.class, MethodHandle.class, ft, int[].class)); 1010 var hasher = permuteArguments(recurHashCode, mtype, index + 1, mhParamCount, mhParamCount + 1); 1011 for (MethodHandle getter : e.getValue()) { 1012 assert ft == fieldType(getter); 1013 var hashThisField = filterArguments(hasher, mhParamCount, getter); 1014 var combineHashes = foldArguments(hashCombiner, 1, hashThisField); 1015 accumulator = foldArguments(combineHashes, 0, accumulator); 1016 } 1017 } 1018 return accumulator; 1019 } 1020 1021 private String debugString() { 1022 StringBuilder sb = new StringBuilder(); 1023 sb.append(type.getName()).append(" ").append(status).append("\n"); 1024 sb.append(fieldValueTypes.stream().filter(c -> !recurFieldTypes.contains(c)) 1025 .map(Class::getName) 1026 .collect(joining(" ", " non-recursive types: ", "\n"))); 1027 sb.append(recurFieldTypes.stream().map(Class::getName) 1028 .collect(joining(" ", " recursive types: ", "\n"))); 1029 for (var n : cyclicMembers.keySet()) { 1030 List<Class<?>> cycle = cyclicMembers.get(n); 1031 sb.append(" cycle: "); 1032 int start = cycle.indexOf(n); 1033 for (int i=start; i < cycle.size(); i++ ) { 1034 sb.append(cycle.get(i).getName()).append(" -> "); 1035 } 1036 for (int i=0; i < start; i++) { 1037 sb.append(cycle.get(i).getName()).append(" -> "); 1038 } 1039 sb.append(n.getName()).append("\n"); 1040 }; 1041 return sb.toString(); 1042 } 1043 } 1044 1045 private static LinkageError newLinkageError(Throwable e) { 1046 return (LinkageError) new LinkageError().initCause(e); 1047 } 1048 1049 /** 1050 * Returns {@code true} if the arguments are <em>substitutable</em> to each 1051 * other and {@code false} otherwise. 1052 * <em>Substitutability</em> means that they cannot be distinguished from 1053 * each other in any data-dependent way, meaning that it is safe to substitute 1054 * one for the other. 1055 * 1056 * <ul> 1057 * <li>If {@code a} and {@code b} are both {@code null}, this method returns 1058 * {@code true}. 1059 * <li>If {@code a} and {@code b} are both instances of the same value class 1060 * {@code V}, this method returns {@code true} if, for all fields {@code f} 1061 * declared in {@code V}, {@code a.f} and {@code b.f} are substitutable. 1062 * <li>If {@code a} and {@code b} are both values of the same builtin primitive type, 1063 * this method returns {@code a == b} with the following exception: 1064 * <ul> 1065 * <li> For primitive types {@code float} and {@code double} the 1066 * comparison uses the raw bits corresponding to {@link Float#floatToRawIntBits(float)} 1067 * and {@link Double#doubleToRawLongBits(double)} respectively. 1068 * </ul> 1069 * <li>If {@code a} and {@code b} are both instances of the same reference type, 1070 * this method returns {@code a == b}. 1071 * <li>Otherwise this method returns {@code false}. 1072 * </ul> 1073 * 1074 * <p>For example, 1075 * <pre>{@code interface Number { ... } 1076 * // ordinary reference class 1077 * class IntNumber implements Number { ... } 1078 * // value class 1079 * value class IntValue implements Number { 1080 * int i; 1081 * : 1082 * public static IntValue of(int i) {...} // IntValue::of creates a new value instance 1083 * } 1084 * // value class with an Object field 1085 * value class RefValue { 1086 * Object o; 1087 * : 1088 * } 1089 * 1090 * var val1 = IntValue.of(10); 1091 * var val2 = IntValue.of(10); // val1 and val2 have the same value 1092 * var ref1 = new IntNumber(10); // ref1 and ref2 are two reference instances 1093 * var ref2 = new IntNumber(10); 1094 * assertTrue(isSubstitutable(val1, val2)); // val1 and val2 are both value instances of IntValue 1095 * assertFalse(isSubstitutable(ref1, ref2)); // ref1 and ref2 are two reference instances that are not substitutable 1096 * assertTrue(isSubstitutable(ref1, ref1)); // a reference instance is substitutable with itself 1097 * 1098 * var rval1 = RefValue.of(List.of("list")); // rval1.o and rval2.o both contain a list of one-single element "list" 1099 * var rval2 = RefValue.of(List.of("list"); 1100 * var rval3 = RefValue.of(rval1.o); 1101 * 1102 * assertFalse(isSubstitutable(rval1, rval2)); // rval1.o and rval2.o are two different List instances and hence not substitutable 1103 * assertTrue(isSubstitutable(rval1, rval3 )); // rval1.o and rval3.o are the same reference instance 1104 * }</pre> 1105 * 1106 * @param a an object 1107 * @param b an object to be compared with {@code a} for substitutability 1108 * @return {@code true} if the arguments are substitutable to each other; 1109 * {@code false} otherwise. 1110 * @param <T> type 1111 * @see Float#floatToRawIntBits(float) 1112 * @see Double#doubleToRawLongBits(double) 1113 */ 1114 private static <T> boolean isSubstitutable(T a, Object b) { 1115 if (VERBOSE) { 1116 System.out.println("substitutable " + a.getClass() + ": " + a + " vs " + b); 1117 } 1118 1119 // Called directly from the VM. 1120 // 1121 // DO NOT use "==" or "!=" on args "a" and "b", with this code or any of 1122 // its callees. Could be inside of if_acmp<eq|ne> bytecode implementation. 1123 1124 if (a == null && b == null) return true; 1125 if (a == null || b == null) return false; 1126 if (a.getClass() != b.getClass()) return false; 1127 1128 try { 1129 Class<?> type = a.getClass(); 1130 return (boolean) substitutableInvoker(type).invoke(a, b); 1131 } catch (Error|RuntimeException e) { 1132 if (VERBOSE) e.printStackTrace(); 1133 throw e; 1134 } catch (Throwable e) { 1135 if (VERBOSE) e.printStackTrace(); 1136 throw new InternalError(e); 1137 } 1138 } 1139 1140 /** 1141 * Produces a method handle which tests if two arguments are 1142 * {@linkplain #isSubstitutable(Object, Object) substitutable}. 1143 * 1144 * <ul> 1145 * <li>If {@code T} is a non-floating point primitive type, this method 1146 * returns a method handle testing the two arguments are the same value, 1147 * i.e. {@code a == b}. 1148 * <li>If {@code T} is {@code float} or {@code double}, this method 1149 * returns a method handle representing {@link Float#floatToRawIntBits(float)} or 1150 * {@link Double#doubleToRawLongBits(double)} respectively. 1151 * <li>If {@code T} is a reference type that is not {@code Object} and not an 1152 * interface, this method returns a method handle testing 1153 * the two arguments are the same reference, i.e. {@code a == b}. 1154 * <li>If {@code T} is a value class, this method returns 1155 * a method handle that returns {@code true} if 1156 * for all fields {@code f} declared in {@code T}, where {@code U} is 1157 * the type of {@code f}, if {@code a.f} and {@code b.f} are substitutable 1158 * with respect to {@code U}. 1159 * <li>If {@code T} is an interface or {@code Object}, and 1160 * {@code a} and {@code b} are of the same value class {@code V}, 1161 * this method returns a method handle that returns {@code true} if 1162 * {@code a} and {@code b} are substitutable with respect to {@code V}. 1163 * </ul> 1164 * 1165 * @param type class type 1166 * @param <T> type 1167 * @return a method handle for substitutability test 1168 */ 1169 private static <T> MethodHandle substitutableInvoker(Class<T> type) { 1170 if (type.isPrimitive()) { 1171 return MethodHandleBuilder.builtinPrimitiveSubstitutable(type); 1172 } 1173 if (ValueClass.isConcreteValueClass(type)) { 1174 return SUBST_TEST_METHOD_HANDLES.get(type); 1175 } 1176 return MethodHandleBuilder.referenceTypeEquals(type); 1177 } 1178 1179 private static final ClassValue<MethodHandleBuilder> METHOD_HANDLE_BUILDERS = new ClassValue<>() { 1180 @Override protected MethodHandleBuilder computeValue(Class<?> type) { 1181 var builder = CACHED_METHOD_HANDLE_BUILDERS.get(type); 1182 if (builder == null) { 1183 builder = MethodHandleBuilder.newBuilder(type).build(); 1184 } 1185 return builder; 1186 } 1187 }; 1188 1189 // This cache is only used to propagate the builders of mutual recursive types 1190 // A -> B -> C -> A as method handles for equals/hashCode for A, B, C are 1191 // all precomputed. This map should only be non-empty only during the short 1192 // window propagating to the method handle builder class value. 1193 private static Map<Class<?>, MethodHandleBuilder> CACHED_METHOD_HANDLE_BUILDERS = new ConcurrentHashMap<>(); 1194 1195 // store the method handle for value classes in ClassValue 1196 private static final ClassValue<MethodHandle> SUBST_TEST_METHOD_HANDLES = new ClassValue<>() { 1197 @Override protected MethodHandle computeValue(Class<?> type) { 1198 return MethodHandleBuilder.valueTypeEquals(type); 1199 } 1200 }; 1201 1202 /** 1203 * Invoke the hashCode method for the given value object. 1204 * @param o the instance to hash. 1205 * @return the hash code of the given value object. 1206 */ 1207 private static int valueObjectHashCode(Object o) { 1208 Class<?> type = o.getClass(); 1209 try { 1210 // Note: javac disallows user to call super.hashCode if user implemented 1211 // risk for recursion for experts crafting byte-code 1212 if (!type.isValue()) 1213 throw new InternalError("must be value class: " + type.getName()); 1214 1215 return (int) HASHCODE_METHOD_HANDLES.get(type).invoke(o); 1216 } catch (Error|RuntimeException e) { 1217 throw e; 1218 } catch (Throwable e) { 1219 if (VERBOSE) e.printStackTrace(); 1220 throw new InternalError(e); 1221 } 1222 } 1223 1224 private static final ClassValue<MethodHandle> HASHCODE_METHOD_HANDLES = new ClassValue<>() { 1225 @Override protected MethodHandle computeValue(Class<?> type) { 1226 return MethodHandleBuilder.valueTypeHashCode(type); 1227 } 1228 }; 1229 1230 private static final Comparator<MethodHandle> TYPE_SORTER = (mh1, mh2) -> { 1231 // sort the getters with the return type 1232 Class<?> t1 = mh1.type().returnType(); 1233 Class<?> t2 = mh2.type().returnType(); 1234 if (t1 == t2) return 0; 1235 1236 if (t1.isPrimitive()) { 1237 if (!t2.isPrimitive()) { 1238 return 1; 1239 } 1240 } else { 1241 if (t2.isPrimitive()) { 1242 return -1; 1243 } 1244 } 1245 return -1; 1246 }; 1247 1248 1249 /** 1250 * Constructs a method handle that is capable of recursively 1251 * calling itself, whose behavior is determined by a non-recursive 1252 * base method handle which gets both the original arguments and a 1253 * reference to the recursive method handle. 1254 * <p> 1255 * Here is pseudocode for the resulting loop handle, plus a sketch 1256 * of the behavior of the base function. The symbols {@code A}, 1257 * {@code a}, and {@code R} represent arguments and return value 1258 * for both the recursive function and the base function. 1259 * 1260 * <blockquote><pre>{@code 1261 * R recursive(A... a) { 1262 * MethodHandle recur = &recursive; 1263 * return base(recur, a...); 1264 * } 1265 * R base(MethodHandle recur, A... a) { 1266 * ... if (no recursion) return f(a); ... 1267 * var r2 = recur.invokeExact(a2...); 1268 * var r3 = recur.invokeExact(a3...); 1269 * ... do stuff with r2, r3, etc. ... 1270 * } 1271 * }</pre></blockquote> 1272 * <p> 1273 * To make several functions mutually recursive, additional base 1274 * arguments can be passed to this combinator. For each base 1275 * function, a recursive adapter is formed (like {@code recur} 1276 * above). The sequence of recursive adapters is passed as 1277 * initial arguments to each base function. Here is pseudocode 1278 * that corresponds to three base functions: 1279 * <blockquote><pre>{@code 1280 * R recursive(A... a) { 1281 * return base(&recursive, &recursive2, &recursive3, a...); 1282 * } 1283 * R2 recursive2(A2... a2) { 1284 * return base2(&recursive, &recursive2, &recursive3, a2...); 1285 * } 1286 * R3 recursive3(A3... a3) { 1287 * return base3(&recursive, &recursive2, &recursive3, a3...); 1288 * } 1289 * R base(MethodHandle recur, MethodHandle recur2, 1290 * MethodHandle recur3, A... a) { 1291 * ... if (no recursion) return f(a); ... 1292 * var r2 = recur2.invokeExact(a2...); 1293 * var r3 = recur3.invokeExact(a3...); 1294 * ... do stuff with r2, r3, etc. ... 1295 * } 1296 * R2 base2(MethodHandle recur, MethodHandle recur2, 1297 * MethodHandle recur3, A2... a2) { ... } 1298 * R3 base3(MethodHandle recur, MethodHandle recur2, 1299 * MethodHandle recur3, A3... a3) { ... } 1300 * }</pre></blockquote> 1301 * 1302 * @apiNote Example: 1303 * {@snippet lang="java" : 1304 * // classic recursive implementation of the factorial function 1305 * static int base(MethodHandle recur, int k) throws Throwable { 1306 * if (k <= 1) return 1; 1307 * return k * (int) recur.invokeExact(k - 1); 1308 * } 1309 * // assume MH_base is a handle to the above method 1310 * MethodHandle recur = MethodHandles.recursive(MH_base); 1311 * assertEquals(120, recur.invoke(5)); 1312 * } 1313 * <p> 1314 * A constructed recursive method handle is made varargs 1315 * if its corresponding base method handle is varargs. 1316 * @implSpec 1317 * For a single base function, this produces a result equivalent to: 1318 * <pre>{@code 1319 * class Holder { 1320 * final MethodHandle recur; 1321 * static final MH_recur = ...; //field getter 1322 * Holder(MethodHandle base) { 1323 * recur = filterArguments(base, 0, MH_recur).bindTo(this); 1324 * } 1325 * } 1326 * return new Holder(base).recur; 1327 * }</pre> 1328 * @param base the logic of the function to make recursive 1329 * @param moreBases additional base functions to be made mutually recursive 1330 * @throws NullPointerException if any argument is null 1331 * @throws IllegalArgumentException if any base function does not accept 1332 * the required leading arguments of type {@code MethodHandle} 1333 * 1334 * @return a method handle which invokes the (first) base function 1335 * on the incoming arguments, with recursive versions of the 1336 * base function (or functions) prepended as extra arguments 1337 * 1338 * @since Valhalla 1339 */ 1340 static MethodHandle recursive(MethodHandle base, MethodHandle... moreBases) { 1341 // freeze the varargs and check for nulls: 1342 List<MethodHandle> bases2 = List.of(moreBases); 1343 int baseCount = 1 + bases2.size(); 1344 recursiveChecks(base, baseCount); 1345 for (var base2 : bases2) { recursiveChecks(base2, baseCount); } 1346 class Holder { 1347 final MethodHandle recur; 1348 final List<MethodHandle> recurs2; 1349 MethodHandle recurs2(int i) { return recurs2.get(i); } 1350 Holder() { 1351 // Feed the first baseCount parameters of each base 1352 // with a fetch of each recur, so we can bind to this: 1353 var fetchers = new MethodHandle[baseCount]; 1354 fetchers[0] = MH_recur; 1355 for (int pos = 1; pos < fetchers.length; pos++) { 1356 int i = pos-1; // index into recurs2 1357 fetchers[pos] = MethodHandles.insertArguments(MH_recurs2, 1, i); 1358 } 1359 this.recur = makeRecur(base, fetchers); 1360 if (baseCount == 1) { 1361 this.recurs2 = List.of(); 1362 } else { 1363 var recurs2 = new MethodHandle[baseCount-1]; 1364 for (int i = 0; i < recurs2.length; i++) { 1365 recurs2[i] = makeRecur(bases2.get(i), fetchers); 1366 } 1367 this.recurs2 = List.of(recurs2); 1368 } 1369 } 1370 MethodHandle makeRecur(MethodHandle base, MethodHandle[] fetchers) { 1371 var adapt = filterArguments(base, 0, fetchers); 1372 for (int pos = 0; pos < fetchers.length; pos++) { 1373 adapt = adapt.bindTo(this); 1374 } 1375 return adapt.withVarargs(base.isVarargsCollector()); 1376 } 1377 static final MethodHandle MH_recur, MH_recurs2; 1378 static { 1379 try { 1380 MH_recur = MethodHandles.lookup() 1381 .findGetter(Holder.class, "recur", MethodHandle.class); 1382 MH_recurs2 = MethodHandles.lookup() 1383 .findVirtual(Holder.class, "recurs2", 1384 methodType(MethodHandle.class, int.class)); 1385 } catch (ReflectiveOperationException ex) { 1386 throw new InternalError(ex); 1387 } 1388 } 1389 } 1390 return new Holder().recur; 1391 } 1392 1393 private static void recursiveChecks(MethodHandle base, int baseCount) { 1394 MethodType mt = base.type(); // implicit null check 1395 boolean wrong = (mt.parameterCount() < baseCount); 1396 for (int i = 0; i < baseCount && !wrong; i++) { 1397 if (mt.parameterType(i) != MethodHandle.class) { 1398 wrong = true; 1399 } 1400 } 1401 if (!wrong) return; 1402 throw new IllegalArgumentException("missing leading MethodHandle parameters: " + mt); 1403 } 1404 1405 }