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