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