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 }