1 /* 2 * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.lang.invoke; 27 28 import sun.invoke.util.Wrapper; 29 30 import java.lang.ref.SoftReference; 31 import java.util.Arrays; 32 import java.util.Comparator; 33 import java.util.TreeMap; 34 import java.util.concurrent.ConcurrentHashMap; 35 36 import static java.lang.invoke.LambdaForm.*; 37 import static java.lang.invoke.LambdaForm.BasicType.*; 38 import static java.lang.invoke.MethodHandleImpl.Intrinsic; 39 import static java.lang.invoke.MethodHandleImpl.NF_loop; 40 import static java.lang.invoke.MethodHandleImpl.makeIntrinsic; 41 import static java.lang.invoke.MethodHandleStatics.NO_SOFT_CACHE; 42 43 /** Transforms on LFs. 44 * A lambda-form editor can derive new LFs from its base LF. 45 * The editor can cache derived LFs, which simplifies the reuse of their underlying bytecodes. 46 * To support this caching, a LF has an optional pointer to its editor. 47 */ 48 class LambdaFormEditor { 49 final LambdaForm lambdaForm; 50 51 private LambdaFormEditor(LambdaForm lambdaForm) { 52 this.lambdaForm = lambdaForm; 53 } 54 55 // Factory method. 56 static LambdaFormEditor lambdaFormEditor(LambdaForm lambdaForm) { 57 // TO DO: Consider placing intern logic here, to cut down on duplication. 58 // lambdaForm = findPreexistingEquivalent(lambdaForm) 59 60 // Always use uncustomized version for editing. 61 // It helps caching and customized LambdaForms reuse transformCache field to keep a link to uncustomized version. 62 return new LambdaFormEditor(lambdaForm.uncustomize()); 63 } 64 65 // Transform types 66 // maybe add more for guard with test, catch exception, pointwise type conversions 67 private static final byte 68 BIND_ARG = 1, 69 ADD_ARG = 2, 70 DUP_ARG = 3, 71 SPREAD_ARGS = 4, 72 FILTER_ARG = 5, 73 FILTER_RETURN = 6, 74 COLLECT_ARGS = 7, 75 COLLECT_ARGS_TO_VOID = 8, 76 REPEAT_FILTER_ARGS = 9, 77 FOLD_ARGS = 10, 78 FOLD_ARGS_TO_VOID = 11, 79 PERMUTE_ARGS = 12, 80 LOCAL_TYPES = 13, 81 FILTER_SELECT_ARGS = 14, 82 FOLD_SELECT_ARGS = 15; 83 84 /** 85 * A description of a cached transform, possibly associated with the result of the transform. 86 * The logical content is a sequence of byte values, starting with a kind value. 87 * The sequence is unterminated, ending with an indefinite number of zero bytes. 88 * Sequences that are simple (short enough and with small enough values) pack into a 64-bit long. 89 * 90 * Tightly coupled with the TransformKey class, which is used to lookup existing 91 * Transforms. 92 */ 93 private static final class Transform { 94 final Object cache; 95 final long packedBytes; 96 final byte[] fullBytes; 97 98 private Transform(long packedBytes, byte[] fullBytes, LambdaForm result) { 99 if (NO_SOFT_CACHE) { 100 cache = result; 101 } else { 102 cache = new SoftReference<LambdaForm>(result); 103 } 104 this.packedBytes = packedBytes; 105 this.fullBytes = fullBytes; 106 } 107 108 @Override 109 public boolean equals(Object obj) { 110 if (obj instanceof TransformKey key) { 111 return equals(key); 112 } 113 return obj instanceof Transform transform && equals(transform); 114 } 115 116 private boolean equals(TransformKey that) { 117 return this.packedBytes == that.packedBytes && Arrays.equals(this.fullBytes, that.fullBytes); 118 } 119 120 private boolean equals(Transform that) { 121 return this.packedBytes == that.packedBytes && Arrays.equals(this.fullBytes, that.fullBytes); 122 } 123 124 @Override 125 public int hashCode() { 126 if (packedBytes != 0) { 127 assert(fullBytes == null); 128 return Long.hashCode(packedBytes); 129 } 130 return Arrays.hashCode(fullBytes); 131 } 132 133 @Override 134 public String toString() { 135 StringBuilder buf = new StringBuilder(); 136 buf.append(new TransformKey(packedBytes, fullBytes).toString()); 137 LambdaForm result = get(); 138 if (result != null) { 139 buf.append(" result="); 140 buf.append(result); 141 } 142 return buf.toString(); 143 } 144 145 @SuppressWarnings({"rawtypes", "unchecked"}) 146 public LambdaForm get() { 147 if (cache instanceof LambdaForm) { 148 return (LambdaForm)cache; 149 } else { 150 return ((SoftReference<LambdaForm>)cache).get(); 151 } 152 } 153 } 154 155 /** 156 * Used as a lookup key to find existing Transforms 157 */ 158 private static final class TransformKey { 159 final long packedBytes; 160 final byte[] fullBytes; 161 162 private TransformKey(long packedBytes) { 163 this.packedBytes = packedBytes; 164 this.fullBytes = null; 165 } 166 167 private TransformKey(byte[] fullBytes) { 168 assert(packedBytes(fullBytes) == 0); 169 this.fullBytes = fullBytes; 170 this.packedBytes = 0; 171 } 172 173 private TransformKey(long packedBytes, byte[] fullBytes) { 174 assert(fullBytes == null || packedBytes == 0); 175 this.fullBytes = fullBytes; 176 this.packedBytes = packedBytes; 177 } 178 179 private static byte bval(int b) { 180 assert((b & 0xFF) == b); // incoming value must fit in *unsigned* byte 181 return (byte)b; 182 } 183 184 private static int ival(int b) { 185 assert((b & 0xFF) == b); // incoming value must fit in *unsigned* byte 186 return b; 187 } 188 189 static TransformKey of(byte k, int b1) { 190 byte b0 = bval(k); 191 if (inRange(b0 | b1)) 192 return new TransformKey(packedBytes(b0, b1)); 193 else 194 return new TransformKey(fullBytes(b0, b1)); 195 } 196 static TransformKey of(byte b0, int b1, int b2) { 197 if (inRange(b0 | b1 | b2)) 198 return new TransformKey(packedBytes(b0, b1, b2)); 199 else 200 return new TransformKey(fullBytes(b0, b1, b2)); 201 } 202 static TransformKey of(byte b0, int b1, int b2, int b3) { 203 if (inRange(b0 | b1 | b2 | b3)) 204 return new TransformKey(packedBytes(b0, b1, b2, b3)); 205 else 206 return new TransformKey(fullBytes(b0, b1, b2, b3)); 207 } 208 static TransformKey of(byte kind, int... b123) { 209 long packedBytes = packedBytes(kind, b123); 210 if (packedBytes != 0) { 211 return new TransformKey(packedBytes); 212 } 213 byte[] fullBytes = new byte[b123.length + 1]; 214 fullBytes[0] = kind; 215 for (int i = 0; i < b123.length; i++) { 216 fullBytes[i + 1] = TransformKey.bval(b123[i]); 217 } 218 return new TransformKey(fullBytes); 219 } 220 static TransformKey of(byte kind, int b1, int... b234) { 221 long packedBytes = packedBytes(kind, b1, b234); 222 if (packedBytes != 0) { 223 return new TransformKey(packedBytes); 224 } 225 byte[] fullBytes = new byte[b234.length + 2]; 226 fullBytes[0] = kind; 227 fullBytes[1] = bval(b1); 228 for (int i = 0; i < b234.length; i++) { 229 fullBytes[i + 2] = TransformKey.bval(b234[i]); 230 } 231 return new TransformKey(fullBytes); 232 } 233 static TransformKey of(byte kind, int b1, int b2, int... b345) { 234 long packedBytes = packedBytes(kind, b1, b2, b345); 235 if (packedBytes != 0) { 236 return new TransformKey(packedBytes); 237 } 238 byte[] fullBytes = new byte[b345.length + 3]; 239 fullBytes[0] = kind; 240 fullBytes[1] = bval(b1); 241 fullBytes[2] = bval(b2); 242 for (int i = 0; i < b345.length; i++) { 243 fullBytes[i + 3] = TransformKey.bval(b345[i]); 244 } 245 return new TransformKey(fullBytes); 246 } 247 248 private static final boolean STRESS_TEST = false; // turn on to disable most packing 249 private static final int 250 PACKED_BYTE_SIZE = (STRESS_TEST ? 2 : 4), 251 PACKED_BYTE_MASK = (1 << PACKED_BYTE_SIZE) - 1, 252 PACKED_BYTE_MAX_LENGTH = (STRESS_TEST ? 3 : 64 / PACKED_BYTE_SIZE); 253 254 private static long packedBytes(byte b0, int b1, int b2, int[] b345) { 255 if (b345.length + 3 > PACKED_BYTE_MAX_LENGTH) 256 return 0; 257 long pb = 0; 258 int bitset = b0 | b1 | b2; 259 for (int i = 0; i < b345.length; i++) { 260 int b = ival(b345[i]); 261 bitset |= b; 262 pb |= (long)b << ((i + 3) * PACKED_BYTE_SIZE); 263 } 264 if (!inRange(bitset)) 265 return 0; 266 pb = pb | packedBytes(b0, b1, b2); 267 return pb; 268 } 269 private static long packedBytes(byte b0, int b1, int[] b234) { 270 if (b234.length + 2 > PACKED_BYTE_MAX_LENGTH) 271 return 0; 272 long pb = 0; 273 int bitset = b0 | b1; 274 for (int i = 0; i < b234.length; i++) { 275 int b = ival(b234[i]); 276 bitset |= b; 277 pb |= (long)b << ((i + 2) * PACKED_BYTE_SIZE); 278 } 279 if (!inRange(bitset)) 280 return 0; 281 pb = pb | packedBytes(b0, b1); 282 return pb; 283 } 284 private static long packedBytes(byte b0, int[] b123) { 285 if (b123.length + 1 > PACKED_BYTE_MAX_LENGTH) 286 return 0; 287 long pb = 0; 288 int bitset = b0; 289 for (int i = 0; i < b123.length; i++) { 290 int b = ival(b123[i]); 291 bitset |= b; 292 pb |= (long)b << ((i + 1) * PACKED_BYTE_SIZE); 293 } 294 if (!inRange(bitset)) 295 return 0; 296 pb = pb | b0; 297 return pb; 298 } 299 300 private static long packedBytes(byte[] bytes) { 301 if (!inRange(bytes[0]) || bytes.length > PACKED_BYTE_MAX_LENGTH) 302 return 0; 303 long pb = 0; 304 int bitset = 0; 305 for (int i = 0; i < bytes.length; i++) { 306 int b = bytes[i] & 0xFF; 307 bitset |= b; 308 pb |= (long)b << (i * PACKED_BYTE_SIZE); 309 } 310 if (!inRange(bitset)) 311 return 0; 312 return pb; 313 } 314 private static long packedBytes(int b0, int b1) { 315 assert(inRange(b0 | b1)); 316 return ( (b0) 317 | (b1 << 1*PACKED_BYTE_SIZE)); 318 } 319 private static long packedBytes(int b0, int b1, int b2) { 320 assert(inRange(b0 | b1 | b2)); 321 return ( (b0) 322 | (b1 << 1*PACKED_BYTE_SIZE) 323 | (b2 << 2*PACKED_BYTE_SIZE)); 324 } 325 private static long packedBytes(int b0, int b1, int b2, int b3) { 326 assert(inRange(b0 | b1 | b2 | b3)); 327 return ( (b0) 328 | (b1 << 1*PACKED_BYTE_SIZE) 329 | (b2 << 2*PACKED_BYTE_SIZE) 330 | (b3 << 3*PACKED_BYTE_SIZE)); 331 } 332 private static boolean inRange(int bitset) { 333 assert((bitset & 0xFF) == bitset); // incoming values must fit in *unsigned* byte 334 return ((bitset & ~PACKED_BYTE_MASK) == 0); 335 } 336 private static byte[] fullBytes(int... byteValues) { 337 byte[] bytes = new byte[byteValues.length]; 338 int i = 0; 339 for (int bv : byteValues) { 340 bytes[i++] = bval(bv); 341 } 342 assert(packedBytes(bytes) == 0); 343 return bytes; 344 } 345 346 Transform withResult(LambdaForm result) { 347 return new Transform(this.packedBytes, this.fullBytes, result); 348 } 349 350 @Override 351 public String toString() { 352 StringBuilder buf = new StringBuilder(); 353 long bits = packedBytes; 354 if (bits != 0) { 355 buf.append("("); 356 while (bits != 0) { 357 buf.append(bits & PACKED_BYTE_MASK); 358 bits >>>= PACKED_BYTE_SIZE; 359 if (bits != 0) buf.append(","); 360 } 361 buf.append(")"); 362 } 363 if (fullBytes != null) { 364 buf.append("unpacked"); 365 buf.append(Arrays.toString(fullBytes)); 366 } 367 return buf.toString(); 368 } 369 370 @Override 371 public boolean equals(Object obj) { 372 if (obj instanceof TransformKey key) { 373 return equals(key); 374 } 375 return obj instanceof Transform transform && equals(transform); 376 } 377 378 private boolean equals(TransformKey that) { 379 return this.packedBytes == that.packedBytes && Arrays.equals(this.fullBytes, that.fullBytes); 380 } 381 382 private boolean equals(Transform that) { 383 return this.packedBytes == that.packedBytes && Arrays.equals(this.fullBytes, that.fullBytes); 384 } 385 386 @Override 387 public int hashCode() { 388 if (packedBytes != 0) { 389 return Long.hashCode(packedBytes); 390 } 391 return Arrays.hashCode(fullBytes); 392 } 393 } 394 395 /** Find a previously cached transform equivalent to the given one, and return its result. */ 396 private LambdaForm getInCache(TransformKey key) { 397 // The transformCache is one of null, Transform, Transform[], or ConcurrentHashMap. 398 Object c = lambdaForm.transformCache; 399 Transform k = null; 400 if (c instanceof ConcurrentHashMap) { 401 @SuppressWarnings("unchecked") 402 ConcurrentHashMap<Transform,Transform> m = (ConcurrentHashMap<Transform,Transform>) c; 403 k = m.get(key); 404 } else if (c == null) { 405 return null; 406 } else if (c instanceof Transform t) { 407 // one-element cache avoids overhead of an array 408 if (t.equals(key)) k = t; 409 } else { 410 Transform[] ta = (Transform[])c; 411 for (int i = 0; i < ta.length; i++) { 412 Transform t = ta[i]; 413 if (t == null) break; 414 if (t.equals(key)) { k = t; break; } 415 } 416 } 417 assert(k == null || key.equals(k)); 418 return (k != null) ? k.get() : null; 419 } 420 421 /** Arbitrary but reasonable limits on Transform[] size for cache. */ 422 private static final int MIN_CACHE_ARRAY_SIZE = 4, MAX_CACHE_ARRAY_SIZE = 16; 423 424 /** Cache a transform with its result, and return that result. 425 * But if an equivalent transform has already been cached, return its result instead. 426 */ 427 private LambdaForm putInCache(TransformKey key, LambdaForm form) { 428 Transform transform = key.withResult(form); 429 for (int pass = 0; ; pass++) { 430 Object c = lambdaForm.transformCache; 431 if (c instanceof ConcurrentHashMap) { 432 @SuppressWarnings("unchecked") 433 ConcurrentHashMap<Transform,Transform> m = (ConcurrentHashMap<Transform,Transform>) c; 434 Transform k = m.putIfAbsent(transform, transform); 435 if (k == null) return form; 436 LambdaForm result = k.get(); 437 if (result != null) { 438 return result; 439 } else { 440 if (m.replace(transform, k, transform)) { 441 return form; 442 } else { 443 continue; 444 } 445 } 446 } 447 assert(pass == 0); 448 synchronized (lambdaForm) { 449 c = lambdaForm.transformCache; 450 if (c instanceof ConcurrentHashMap) 451 continue; 452 if (c == null) { 453 lambdaForm.transformCache = transform; 454 return form; 455 } 456 Transform[] ta; 457 if (c instanceof Transform k) { 458 if (k.equals(key)) { 459 LambdaForm result = k.get(); 460 if (result == null) { 461 lambdaForm.transformCache = transform; 462 return form; 463 } else { 464 return result; 465 } 466 } else if (k.get() == null) { // overwrite stale entry 467 lambdaForm.transformCache = transform; 468 return form; 469 } 470 // expand one-element cache to small array 471 ta = new Transform[MIN_CACHE_ARRAY_SIZE]; 472 ta[0] = k; 473 lambdaForm.transformCache = ta; 474 } else { 475 // it is already expanded 476 ta = (Transform[])c; 477 } 478 int len = ta.length; 479 int stale = -1; 480 int i; 481 for (i = 0; i < len; i++) { 482 Transform k = ta[i]; 483 if (k == null) { 484 break; 485 } 486 if (k.equals(transform)) { 487 LambdaForm result = k.get(); 488 if (result == null) { 489 ta[i] = transform; 490 return form; 491 } else { 492 return result; 493 } 494 } else if (stale < 0 && k.get() == null) { 495 stale = i; // remember 1st stale entry index 496 } 497 } 498 if (i < len || stale >= 0) { 499 // just fall through to cache update 500 } else if (len < MAX_CACHE_ARRAY_SIZE) { 501 len = Math.min(len * 2, MAX_CACHE_ARRAY_SIZE); 502 ta = Arrays.copyOf(ta, len); 503 lambdaForm.transformCache = ta; 504 } else { 505 ConcurrentHashMap<Transform, Transform> m = new ConcurrentHashMap<>(MAX_CACHE_ARRAY_SIZE * 2); 506 for (Transform k : ta) { 507 m.put(k, k); 508 } 509 lambdaForm.transformCache = m; 510 // The second iteration will update for this query, concurrently. 511 continue; 512 } 513 int idx = (stale >= 0) ? stale : i; 514 ta[idx] = transform; 515 return form; 516 } 517 } 518 } 519 520 private LambdaFormBuffer buffer() { 521 return new LambdaFormBuffer(lambdaForm); 522 } 523 524 /// Editing methods for method handles. These need to have fast paths. 525 526 private BoundMethodHandle.SpeciesData oldSpeciesData() { 527 return BoundMethodHandle.speciesDataFor(lambdaForm); 528 } 529 530 private BoundMethodHandle.SpeciesData newSpeciesData(BasicType type) { 531 return oldSpeciesData().extendWith(type); 532 } 533 534 BoundMethodHandle bindArgumentL(BoundMethodHandle mh, int pos, Object value) { 535 assert(mh.speciesData() == oldSpeciesData()); 536 BasicType bt = L_TYPE; 537 MethodType type2 = bindArgumentType(mh, pos, bt); 538 LambdaForm form2 = bindArgumentForm(1+pos); 539 return mh.copyWithExtendL(type2, form2, value); 540 } 541 BoundMethodHandle bindArgumentI(BoundMethodHandle mh, int pos, int value) { 542 assert(mh.speciesData() == oldSpeciesData()); 543 BasicType bt = I_TYPE; 544 MethodType type2 = bindArgumentType(mh, pos, bt); 545 LambdaForm form2 = bindArgumentForm(1+pos); 546 return mh.copyWithExtendI(type2, form2, value); 547 } 548 549 BoundMethodHandle bindArgumentJ(BoundMethodHandle mh, int pos, long value) { 550 assert(mh.speciesData() == oldSpeciesData()); 551 BasicType bt = J_TYPE; 552 MethodType type2 = bindArgumentType(mh, pos, bt); 553 LambdaForm form2 = bindArgumentForm(1+pos); 554 return mh.copyWithExtendJ(type2, form2, value); 555 } 556 557 BoundMethodHandle bindArgumentF(BoundMethodHandle mh, int pos, float value) { 558 assert(mh.speciesData() == oldSpeciesData()); 559 BasicType bt = F_TYPE; 560 MethodType type2 = bindArgumentType(mh, pos, bt); 561 LambdaForm form2 = bindArgumentForm(1+pos); 562 return mh.copyWithExtendF(type2, form2, value); 563 } 564 565 BoundMethodHandle bindArgumentD(BoundMethodHandle mh, int pos, double value) { 566 assert(mh.speciesData() == oldSpeciesData()); 567 BasicType bt = D_TYPE; 568 MethodType type2 = bindArgumentType(mh, pos, bt); 569 LambdaForm form2 = bindArgumentForm(1+pos); 570 return mh.copyWithExtendD(type2, form2, value); 571 } 572 573 private MethodType bindArgumentType(BoundMethodHandle mh, int pos, BasicType bt) { 574 assert(mh.form.uncustomize() == lambdaForm); 575 assert(mh.form.names[1+pos].type == bt); 576 assert(BasicType.basicType(mh.type().parameterType(pos)) == bt); 577 return mh.type().dropParameterTypes(pos, pos+1); 578 } 579 580 /// Editing methods for lambda forms. 581 // Each editing method can (potentially) cache the edited LF so that it can be reused later. 582 583 LambdaForm bindArgumentForm(int pos) { 584 TransformKey key = TransformKey.of(BIND_ARG, pos); 585 LambdaForm form = getInCache(key); 586 if (form != null) { 587 assert(form.parameterConstraint(0) == newSpeciesData(lambdaForm.parameterType(pos))); 588 return form; 589 } 590 LambdaFormBuffer buf = buffer(); 591 buf.startEdit(); 592 593 BoundMethodHandle.SpeciesData oldData = oldSpeciesData(); 594 BoundMethodHandle.SpeciesData newData = newSpeciesData(lambdaForm.parameterType(pos)); 595 Name oldBaseAddress = lambdaForm.parameter(0); // BMH holding the values 596 Name newBaseAddress; 597 NamedFunction getter = newData.getterFunction(oldData.fieldCount()); 598 599 if (pos != 0) { 600 // The newly created LF will run with a different BMH. 601 // Switch over any pre-existing BMH field references to the new BMH class. 602 buf.replaceFunctions(oldData.getterFunctions(), newData.getterFunctions(), oldBaseAddress); 603 newBaseAddress = oldBaseAddress.withConstraint(newData); 604 buf.renameParameter(0, newBaseAddress); 605 buf.replaceParameterByNewExpression(pos, new Name(getter, newBaseAddress)); 606 } else { 607 // cannot bind the MH arg itself, unless oldData is empty 608 assert(oldData == BoundMethodHandle.SPECIALIZER.topSpecies()); 609 newBaseAddress = new Name(L_TYPE).withConstraint(newData); 610 buf.replaceParameterByNewExpression(0, new Name(getter, newBaseAddress)); 611 buf.insertParameter(0, newBaseAddress); 612 } 613 614 form = buf.endEdit(); 615 return putInCache(key, form); 616 } 617 618 LambdaForm addArgumentForm(int pos, BasicType type) { 619 TransformKey key = TransformKey.of(ADD_ARG, pos, type.ordinal()); 620 LambdaForm form = getInCache(key); 621 if (form != null) { 622 assert(form.arity == lambdaForm.arity+1); 623 assert(form.parameterType(pos) == type); 624 return form; 625 } 626 LambdaFormBuffer buf = buffer(); 627 buf.startEdit(); 628 629 buf.insertParameter(pos, new Name(type)); 630 631 form = buf.endEdit(); 632 return putInCache(key, form); 633 } 634 635 LambdaForm dupArgumentForm(int srcPos, int dstPos) { 636 TransformKey key = TransformKey.of(DUP_ARG, srcPos, dstPos); 637 LambdaForm form = getInCache(key); 638 if (form != null) { 639 assert(form.arity == lambdaForm.arity-1); 640 return form; 641 } 642 LambdaFormBuffer buf = buffer(); 643 buf.startEdit(); 644 645 assert(lambdaForm.parameter(srcPos).constraint == null); 646 assert(lambdaForm.parameter(dstPos).constraint == null); 647 buf.replaceParameterByCopy(dstPos, srcPos); 648 649 form = buf.endEdit(); 650 return putInCache(key, form); 651 } 652 653 LambdaForm spreadArgumentsForm(int pos, Class<?> arrayType, int arrayLength) { 654 Class<?> elementType = arrayType.getComponentType(); 655 Class<?> erasedArrayType = arrayType; 656 if (!elementType.isPrimitive()) 657 erasedArrayType = Object[].class; 658 BasicType bt = basicType(elementType); 659 int elementTypeKey = bt.ordinal(); 660 if (bt.basicTypeClass() != elementType) { 661 if (elementType.isPrimitive()) { 662 elementTypeKey = TYPE_LIMIT + Wrapper.forPrimitiveType(elementType).ordinal(); 663 } 664 } 665 TransformKey key = TransformKey.of(SPREAD_ARGS, pos, elementTypeKey, arrayLength); 666 LambdaForm form = getInCache(key); 667 if (form != null) { 668 assert(form.arity == lambdaForm.arity - arrayLength + 1); 669 return form; 670 } 671 LambdaFormBuffer buf = buffer(); 672 buf.startEdit(); 673 674 assert(pos <= MethodType.MAX_JVM_ARITY); 675 assert(pos + arrayLength <= lambdaForm.arity); 676 assert(pos > 0); // cannot spread the MH arg itself 677 678 Name spreadParam = new Name(L_TYPE); 679 Name checkSpread = new Name(MethodHandleImpl.getFunction(MethodHandleImpl.NF_checkSpreadArgument), 680 spreadParam, arrayLength); 681 682 // insert the new expressions 683 int exprPos = lambdaForm.arity(); 684 buf.insertExpression(exprPos++, checkSpread); 685 // adjust the arguments 686 MethodHandle aload = MethodHandles.arrayElementGetter(erasedArrayType); 687 for (int i = 0; i < arrayLength; i++) { 688 Name loadArgument = new Name(new NamedFunction(makeIntrinsic(aload, Intrinsic.ARRAY_LOAD)), spreadParam, i); 689 buf.insertExpression(exprPos + i, loadArgument); 690 buf.replaceParameterByCopy(pos + i, exprPos + i); 691 } 692 buf.insertParameter(pos, spreadParam); 693 694 form = buf.endEdit(); 695 return putInCache(key, form); 696 } 697 698 LambdaForm collectArgumentsForm(int pos, MethodType collectorType) { 699 int collectorArity = collectorType.parameterCount(); 700 boolean dropResult = (collectorType.returnType() == void.class); 701 if (collectorArity == 1 && !dropResult) { 702 return filterArgumentForm(pos, basicType(collectorType.parameterType(0))); 703 } 704 int[] newTypes = BasicType.basicTypesOrd(collectorType.ptypes()); 705 byte kind = (dropResult ? COLLECT_ARGS_TO_VOID : COLLECT_ARGS); 706 if (dropResult && collectorArity == 0) pos = 1; // pure side effect 707 TransformKey key = TransformKey.of(kind, pos, collectorArity, newTypes); 708 LambdaForm form = getInCache(key); 709 if (form != null) { 710 assert(form.arity == lambdaForm.arity - (dropResult ? 0 : 1) + collectorArity); 711 return form; 712 } 713 form = makeArgumentCombinationForm(pos, collectorType, false, dropResult); 714 return putInCache(key, form); 715 } 716 717 LambdaForm filterArgumentForm(int pos, BasicType newType) { 718 TransformKey key = TransformKey.of(FILTER_ARG, pos, newType.ordinal()); 719 LambdaForm form = getInCache(key); 720 if (form != null) { 721 assert(form.arity == lambdaForm.arity); 722 assert(form.parameterType(pos) == newType); 723 return form; 724 } 725 726 BasicType oldType = lambdaForm.parameterType(pos); 727 MethodType filterType = MethodType.methodType(oldType.basicTypeClass(), 728 newType.basicTypeClass()); 729 form = makeArgumentCombinationForm(pos, filterType, false, false); 730 return putInCache(key, form); 731 } 732 733 /** 734 * This creates a LF that will repeatedly invoke some unary filter function 735 * at each of the given positions. This allows fewer LFs and BMH species 736 * classes to be generated in typical cases compared to building up the form 737 * by reapplying of {@code filterArgumentForm(int,BasicType)}, and should do 738 * no worse in the worst case. 739 */ 740 LambdaForm filterRepeatedArgumentForm(BasicType newType, int... argPositions) { 741 assert (argPositions.length > 1); 742 TransformKey key = TransformKey.of(REPEAT_FILTER_ARGS, newType.ordinal(), argPositions); 743 LambdaForm form = getInCache(key); 744 if (form != null) { 745 assert(form.arity == lambdaForm.arity && 746 formParametersMatch(form, newType, argPositions)); 747 return form; 748 } 749 BasicType oldType = lambdaForm.parameterType(argPositions[0]); 750 MethodType filterType = MethodType.methodType(oldType.basicTypeClass(), 751 newType.basicTypeClass()); 752 form = makeRepeatedFilterForm(filterType, argPositions); 753 assert (formParametersMatch(form, newType, argPositions)); 754 return putInCache(key, form); 755 } 756 757 private boolean formParametersMatch(LambdaForm form, BasicType newType, int... argPositions) { 758 for (int i : argPositions) { 759 if (form.parameterType(i) != newType) { 760 return false; 761 } 762 } 763 return true; 764 } 765 766 private LambdaForm makeRepeatedFilterForm(MethodType combinerType, int... positions) { 767 assert (combinerType.parameterCount() == 1 && 768 combinerType == combinerType.basicType() && 769 combinerType.returnType() != void.class); 770 LambdaFormBuffer buf = buffer(); 771 buf.startEdit(); 772 773 BoundMethodHandle.SpeciesData oldData = oldSpeciesData(); 774 BoundMethodHandle.SpeciesData newData = newSpeciesData(L_TYPE); 775 776 // The newly created LF will run with a different BMH. 777 // Switch over any pre-existing BMH field references to the new BMH class. 778 Name oldBaseAddress = lambdaForm.parameter(0); // BMH holding the values 779 buf.replaceFunctions(oldData.getterFunctions(), newData.getterFunctions(), oldBaseAddress); 780 Name newBaseAddress = oldBaseAddress.withConstraint(newData); 781 buf.renameParameter(0, newBaseAddress); 782 783 // Insert the new expressions at the end 784 int exprPos = lambdaForm.arity(); 785 Name getCombiner = new Name(newData.getterFunction(oldData.fieldCount()), newBaseAddress); 786 buf.insertExpression(exprPos++, getCombiner); 787 788 // After inserting expressions, we insert parameters in order 789 // from lowest to highest, simplifying the calculation of where parameters 790 // and expressions are 791 var newParameters = new TreeMap<Name, Integer>(new Comparator<>() { 792 public int compare(Name n1, Name n2) { 793 return n1.index - n2.index; 794 } 795 }); 796 797 // Insert combiner expressions in reverse order so that the invocation of 798 // the resulting form will invoke the combiners in left-to-right order 799 for (int i = positions.length - 1; i >= 0; --i) { 800 int pos = positions[i]; 801 assert (pos > 0 && pos <= MethodType.MAX_JVM_ARITY && pos < lambdaForm.arity); 802 803 Name newParameter = new Name(pos, basicType(combinerType.parameterType(0))); 804 Object[] combinerArgs = {getCombiner, newParameter}; 805 806 Name callCombiner = new Name(combinerType, combinerArgs); 807 buf.insertExpression(exprPos++, callCombiner); 808 newParameters.put(newParameter, exprPos); 809 } 810 811 // Mix in new parameters from left to right in the buffer (this doesn't change 812 // execution order 813 int offset = 0; 814 for (var entry : newParameters.entrySet()) { 815 Name newParameter = entry.getKey(); 816 int from = entry.getValue(); 817 buf.insertParameter(newParameter.index() + 1 + offset, newParameter); 818 buf.replaceParameterByCopy(newParameter.index() + offset, from + offset); 819 offset++; 820 } 821 return buf.endEdit(); 822 } 823 824 825 private LambdaForm makeArgumentCombinationForm(int pos, 826 MethodType combinerType, 827 boolean keepArguments, boolean dropResult) { 828 LambdaFormBuffer buf = buffer(); 829 buf.startEdit(); 830 int combinerArity = combinerType.parameterCount(); 831 int resultArity = (dropResult ? 0 : 1); 832 833 assert(pos <= MethodType.MAX_JVM_ARITY); 834 assert(pos + resultArity + (keepArguments ? combinerArity : 0) <= lambdaForm.arity); 835 assert(pos > 0); // cannot filter the MH arg itself 836 assert(combinerType == combinerType.basicType()); 837 assert(combinerType.returnType() != void.class || dropResult); 838 839 BoundMethodHandle.SpeciesData oldData = oldSpeciesData(); 840 BoundMethodHandle.SpeciesData newData = newSpeciesData(L_TYPE); 841 842 // The newly created LF will run with a different BMH. 843 // Switch over any pre-existing BMH field references to the new BMH class. 844 Name oldBaseAddress = lambdaForm.parameter(0); // BMH holding the values 845 buf.replaceFunctions(oldData.getterFunctions(), newData.getterFunctions(), oldBaseAddress); 846 Name newBaseAddress = oldBaseAddress.withConstraint(newData); 847 buf.renameParameter(0, newBaseAddress); 848 849 Name getCombiner = new Name(newData.getterFunction(oldData.fieldCount()), newBaseAddress); 850 Object[] combinerArgs = new Object[1 + combinerArity]; 851 combinerArgs[0] = getCombiner; 852 Name[] newParams; 853 if (keepArguments) { 854 newParams = new Name[0]; 855 System.arraycopy(lambdaForm.names, pos + resultArity, 856 combinerArgs, 1, combinerArity); 857 } else { 858 newParams = new Name[combinerArity]; 859 for (int i = 0; i < newParams.length; i++) { 860 newParams[i] = new Name(pos + i, basicType(combinerType.parameterType(i))); 861 } 862 System.arraycopy(newParams, 0, 863 combinerArgs, 1, combinerArity); 864 } 865 Name callCombiner = new Name(combinerType, combinerArgs); 866 867 // insert the two new expressions 868 int exprPos = lambdaForm.arity(); 869 buf.insertExpression(exprPos+0, getCombiner); 870 buf.insertExpression(exprPos+1, callCombiner); 871 872 // insert new arguments, if needed 873 int argPos = pos + resultArity; // skip result parameter 874 for (Name newParam : newParams) { 875 buf.insertParameter(argPos++, newParam); 876 } 877 assert(buf.lastIndexOf(callCombiner) == exprPos+1+newParams.length); 878 if (!dropResult) { 879 buf.replaceParameterByCopy(pos, exprPos+1+newParams.length); 880 } 881 882 return buf.endEdit(); 883 } 884 885 private LambdaForm makeArgumentCombinationForm(int pos, 886 MethodType combinerType, 887 int[] argPositions, 888 boolean keepArguments, 889 boolean dropResult) { 890 LambdaFormBuffer buf = buffer(); 891 buf.startEdit(); 892 int combinerArity = combinerType.parameterCount(); 893 assert(combinerArity == argPositions.length); 894 895 int resultArity = (dropResult ? 0 : 1); 896 897 assert(pos <= lambdaForm.arity); 898 assert(pos > 0); // cannot filter the MH arg itself 899 assert(combinerType == combinerType.basicType()); 900 assert(combinerType.returnType() != void.class || dropResult); 901 902 BoundMethodHandle.SpeciesData oldData = oldSpeciesData(); 903 BoundMethodHandle.SpeciesData newData = newSpeciesData(L_TYPE); 904 905 // The newly created LF will run with a different BMH. 906 // Switch over any pre-existing BMH field references to the new BMH class. 907 Name oldBaseAddress = lambdaForm.parameter(0); // BMH holding the values 908 buf.replaceFunctions(oldData.getterFunctions(), newData.getterFunctions(), oldBaseAddress); 909 Name newBaseAddress = oldBaseAddress.withConstraint(newData); 910 buf.renameParameter(0, newBaseAddress); 911 912 Name getCombiner = new Name(newData.getterFunction(oldData.fieldCount()), newBaseAddress); 913 Object[] combinerArgs = new Object[1 + combinerArity]; 914 combinerArgs[0] = getCombiner; 915 Name newParam = null; 916 if (keepArguments) { 917 for (int i = 0; i < combinerArity; i++) { 918 combinerArgs[i + 1] = lambdaForm.parameter(1 + argPositions[i]); 919 assert (basicType(combinerType.parameterType(i)) == lambdaForm.parameterType(1 + argPositions[i])); 920 } 921 } else { 922 newParam = new Name(pos, BasicType.basicType(combinerType.returnType())); 923 for (int i = 0; i < combinerArity; i++) { 924 int argPos = 1 + argPositions[i]; 925 if (argPos == pos) { 926 combinerArgs[i + 1] = newParam; 927 } else { 928 combinerArgs[i + 1] = lambdaForm.parameter(argPos); 929 } 930 assert (basicType(combinerType.parameterType(i)) == lambdaForm.parameterType(1 + argPositions[i])); 931 } 932 } 933 Name callCombiner = new Name(combinerType, combinerArgs); 934 935 // insert the two new expressions 936 int exprPos = lambdaForm.arity(); 937 buf.insertExpression(exprPos+0, getCombiner); 938 buf.insertExpression(exprPos+1, callCombiner); 939 940 // insert new arguments, if needed 941 int argPos = pos + resultArity; // skip result parameter 942 if (newParam != null) { 943 buf.insertParameter(argPos++, newParam); 944 exprPos++; 945 } 946 assert(buf.lastIndexOf(callCombiner) == exprPos+1); 947 if (!dropResult) { 948 buf.replaceParameterByCopy(pos, exprPos+1); 949 } 950 951 return buf.endEdit(); 952 } 953 954 LambdaForm filterReturnForm(BasicType newType, boolean constantZero) { 955 TransformKey key = TransformKey.of(FILTER_RETURN, constantZero ? (byte) 1 : (byte)0, newType.ordinal()); 956 LambdaForm form = getInCache(key); 957 if (form != null) { 958 assert(form.arity == lambdaForm.arity); 959 assert(form.returnType() == newType); 960 return form; 961 } 962 LambdaFormBuffer buf = buffer(); 963 buf.startEdit(); 964 965 int insPos = lambdaForm.names.length; 966 Name callFilter; 967 if (constantZero) { 968 // Synthesize a constant zero value for the given type. 969 if (newType == V_TYPE) 970 callFilter = null; 971 else 972 callFilter = new Name(constantZero(newType)); 973 } else { 974 BoundMethodHandle.SpeciesData oldData = oldSpeciesData(); 975 BoundMethodHandle.SpeciesData newData = newSpeciesData(L_TYPE); 976 977 // The newly created LF will run with a different BMH. 978 // Switch over any pre-existing BMH field references to the new BMH class. 979 Name oldBaseAddress = lambdaForm.parameter(0); // BMH holding the values 980 buf.replaceFunctions(oldData.getterFunctions(), newData.getterFunctions(), oldBaseAddress); 981 Name newBaseAddress = oldBaseAddress.withConstraint(newData); 982 buf.renameParameter(0, newBaseAddress); 983 984 Name getFilter = new Name(newData.getterFunction(oldData.fieldCount()), newBaseAddress); 985 buf.insertExpression(insPos++, getFilter); 986 BasicType oldType = lambdaForm.returnType(); 987 if (oldType == V_TYPE) { 988 MethodType filterType = MethodType.methodType(newType.basicTypeClass()); 989 callFilter = new Name(filterType, getFilter); 990 } else { 991 MethodType filterType = MethodType.methodType(newType.basicTypeClass(), oldType.basicTypeClass()); 992 callFilter = new Name(filterType, getFilter, lambdaForm.names[lambdaForm.result]); 993 } 994 } 995 996 if (callFilter != null) 997 buf.insertExpression(insPos++, callFilter); 998 buf.setResult(callFilter); 999 1000 form = buf.endEdit(); 1001 return putInCache(key, form); 1002 } 1003 1004 LambdaForm collectReturnValueForm(MethodType combinerType) { 1005 LambdaFormBuffer buf = buffer(); 1006 buf.startEdit(); 1007 int combinerArity = combinerType.parameterCount(); 1008 int argPos = lambdaForm.arity(); 1009 int exprPos = lambdaForm.names.length; 1010 1011 BoundMethodHandle.SpeciesData oldData = oldSpeciesData(); 1012 BoundMethodHandle.SpeciesData newData = newSpeciesData(L_TYPE); 1013 1014 // The newly created LF will run with a different BMH. 1015 // Switch over any pre-existing BMH field references to the new BMH class. 1016 Name oldBaseAddress = lambdaForm.parameter(0); // BMH holding the values 1017 buf.replaceFunctions(oldData.getterFunctions(), newData.getterFunctions(), oldBaseAddress); 1018 Name newBaseAddress = oldBaseAddress.withConstraint(newData); 1019 buf.renameParameter(0, newBaseAddress); 1020 1021 // Now we set up the call to the filter 1022 Name getCombiner = new Name(newData.getterFunction(oldData.fieldCount()), newBaseAddress); 1023 1024 Object[] combinerArgs = new Object[combinerArity + 1]; 1025 combinerArgs[0] = getCombiner; // first (synthetic) argument should be the MH that acts as a target of the invoke 1026 1027 // set up additional adapter parameters (in case the combiner is not a unary function) 1028 Name[] newParams = new Name[combinerArity - 1]; // last combiner parameter is the return adapter 1029 for (int i = 0; i < newParams.length; i++) { 1030 newParams[i] = new Name(argPos + i, basicType(combinerType.parameterType(i))); 1031 } 1032 1033 // set up remaining filter parameters to point to the corresponding adapter parameters (see above) 1034 System.arraycopy(newParams, 0, 1035 combinerArgs, 1, combinerArity - 1); 1036 1037 // the last filter argument is set to point at the result of the target method handle 1038 combinerArgs[combinerArity] = buf.name(lambdaForm.names.length - 1); 1039 Name callCombiner = new Name(combinerType, combinerArgs); 1040 1041 // insert the two new expressions 1042 buf.insertExpression(exprPos, getCombiner); 1043 buf.insertExpression(exprPos + 1, callCombiner); 1044 1045 // insert additional arguments 1046 int insPos = argPos; 1047 for (Name newParam : newParams) { 1048 buf.insertParameter(insPos++, newParam); 1049 } 1050 1051 buf.setResult(callCombiner); 1052 return buf.endEdit(); 1053 } 1054 1055 LambdaForm foldArgumentsForm(int foldPos, boolean dropResult, MethodType combinerType) { 1056 int combinerArity = combinerType.parameterCount(); 1057 byte kind = (dropResult ? FOLD_ARGS_TO_VOID : FOLD_ARGS); 1058 TransformKey key = TransformKey.of(kind, foldPos, combinerArity); 1059 LambdaForm form = getInCache(key); 1060 if (form != null) { 1061 assert(form.arity == lambdaForm.arity - (kind == FOLD_ARGS ? 1 : 0)); 1062 return form; 1063 } 1064 form = makeArgumentCombinationForm(foldPos, combinerType, true, dropResult); 1065 return putInCache(key, form); 1066 } 1067 1068 LambdaForm foldArgumentsForm(int foldPos, boolean dropResult, MethodType combinerType, int ... argPositions) { 1069 TransformKey key = TransformKey.of(FOLD_SELECT_ARGS, foldPos, dropResult ? 1 : 0, argPositions); 1070 LambdaForm form = getInCache(key); 1071 if (form != null) { 1072 assert(form.arity == lambdaForm.arity - (dropResult ? 0 : 1)); 1073 return form; 1074 } 1075 form = makeArgumentCombinationForm(foldPos, combinerType, argPositions, true, dropResult); 1076 return putInCache(key, form); 1077 } 1078 1079 LambdaForm filterArgumentsForm(int filterPos, MethodType combinerType, int ... argPositions) { 1080 TransformKey key = TransformKey.of(FILTER_SELECT_ARGS, filterPos, argPositions); 1081 LambdaForm form = getInCache(key); 1082 if (form != null) { 1083 assert(form.arity == lambdaForm.arity); 1084 return form; 1085 } 1086 form = makeArgumentCombinationForm(filterPos, combinerType, argPositions, false, false); 1087 return putInCache(key, form); 1088 } 1089 1090 LambdaForm permuteArgumentsForm(int skip, int[] reorder) { 1091 assert(skip == 1); // skip only the leading MH argument, names[0] 1092 int length = lambdaForm.names.length; 1093 int outArgs = reorder.length; 1094 int inTypes = 0; 1095 boolean nullPerm = true; 1096 for (int i = 0; i < reorder.length; i++) { 1097 int inArg = reorder[i]; 1098 if (inArg != i) nullPerm = false; 1099 inTypes = Math.max(inTypes, inArg+1); 1100 } 1101 assert(skip + reorder.length == lambdaForm.arity); 1102 if (nullPerm) return lambdaForm; // do not bother to cache 1103 TransformKey key = TransformKey.of(PERMUTE_ARGS, reorder); 1104 LambdaForm form = getInCache(key); 1105 if (form != null) { 1106 assert(form.arity == skip+inTypes) : form; 1107 return form; 1108 } 1109 1110 BasicType[] types = new BasicType[inTypes]; 1111 for (int i = 0; i < outArgs; i++) { 1112 int inArg = reorder[i]; 1113 types[inArg] = lambdaForm.names[skip + i].type; 1114 } 1115 assert (skip + outArgs == lambdaForm.arity); 1116 assert (permutedTypesMatch(reorder, types, lambdaForm.names, skip)); 1117 int pos = 0; 1118 while (pos < outArgs && reorder[pos] == pos) { 1119 pos += 1; 1120 } 1121 Name[] names2 = new Name[length - outArgs + inTypes]; 1122 System.arraycopy(lambdaForm.names, 0, names2, 0, skip + pos); 1123 int bodyLength = length - lambdaForm.arity; 1124 System.arraycopy(lambdaForm.names, skip + outArgs, names2, skip + inTypes, bodyLength); 1125 int arity2 = names2.length - bodyLength; 1126 int result2 = lambdaForm.result; 1127 if (result2 >= skip) { 1128 if (result2 < skip + outArgs) { 1129 result2 = reorder[result2 - skip] + skip; 1130 } else { 1131 result2 = result2 - outArgs + inTypes; 1132 } 1133 } 1134 for (int j = pos; j < outArgs; j++) { 1135 Name n = lambdaForm.names[skip + j]; 1136 int i = reorder[j]; 1137 Name n2 = names2[skip + i]; 1138 if (n2 == null) { 1139 names2[skip + i] = n2 = new Name(types[i]); 1140 } else { 1141 assert (n2.type == types[i]); 1142 } 1143 for (int k = arity2; k < names2.length; k++) { 1144 names2[k] = names2[k].replaceName(n, n2); 1145 } 1146 } 1147 for (int i = skip + pos; i < arity2; i++) { 1148 if (names2[i] == null) { 1149 names2[i] = argument(i, types[i - skip]); 1150 } 1151 } 1152 for (int j = lambdaForm.arity; j < lambdaForm.names.length; j++) { 1153 int i = j - lambdaForm.arity + arity2; 1154 Name n = lambdaForm.names[j]; 1155 Name n2 = names2[i]; 1156 if (n != n2) { 1157 for (int k = i + 1; k < names2.length; k++) { 1158 names2[k] = names2[k].replaceName(n, n2); 1159 } 1160 } 1161 } 1162 1163 form = LambdaForm.create(arity2, names2, result2); 1164 return putInCache(key, form); 1165 } 1166 1167 LambdaForm noteLoopLocalTypesForm(int pos, BasicType[] localTypes) { 1168 assert(lambdaForm.isLoop(pos)); 1169 int[] desc = BasicType.basicTypeOrds(localTypes); 1170 desc = Arrays.copyOf(desc, desc.length + 1); 1171 desc[desc.length - 1] = pos; 1172 TransformKey key = TransformKey.of(LOCAL_TYPES, desc); 1173 LambdaForm form = getInCache(key); 1174 if (form != null) { 1175 return form; 1176 } 1177 1178 // replace the null entry in the MHImpl.loop invocation with localTypes 1179 Name invokeLoop = lambdaForm.names[pos + 1]; 1180 assert(invokeLoop.function.equals(MethodHandleImpl.getFunction(NF_loop))); 1181 Object[] args = Arrays.copyOf(invokeLoop.arguments, invokeLoop.arguments.length); 1182 assert(args[0] == null); 1183 args[0] = localTypes; 1184 1185 LambdaFormBuffer buf = buffer(); 1186 buf.startEdit(); 1187 buf.changeName(pos + 1, new Name(MethodHandleImpl.getFunction(NF_loop), args)); 1188 form = buf.endEdit(); 1189 1190 return putInCache(key, form); 1191 } 1192 1193 static boolean permutedTypesMatch(int[] reorder, BasicType[] types, Name[] names, int skip) { 1194 for (int i = 0; i < reorder.length; i++) { 1195 assert (names[skip + i].isParam()); 1196 assert (names[skip + i].type == types[reorder[i]]); 1197 } 1198 return true; 1199 } 1200 }