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