1 /*
   2  * Copyright (c) 2014, 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 java.lang.ref.SoftReference;
  29 import java.util.Arrays;
  30 import static java.lang.invoke.LambdaForm.*;
  31 import static java.lang.invoke.LambdaForm.BasicType.*;
  32 import static java.lang.invoke.MethodHandleImpl.Intrinsic;
  33 import java.util.Collections;
  34 import java.util.concurrent.ConcurrentHashMap;
  35 
  36 import sun.invoke.util.Wrapper;
  37 
  38 /** Transforms on LFs.
  39  *  A lambda-form editor can derive new LFs from its base LF.
  40  *  The editor can cache derived LFs, which simplifies the reuse of their underlying bytecodes.
  41  *  To support this caching, a LF has an optional pointer to its editor.
  42  */
  43 class LambdaFormEditor {
  44     final LambdaForm lambdaForm;
  45 
  46     private LambdaFormEditor(LambdaForm lambdaForm) {
  47         this.lambdaForm = lambdaForm;
  48     }
  49 
  50     // Factory method.
  51     static LambdaFormEditor lambdaFormEditor(LambdaForm lambdaForm) {
  52         // TO DO:  Consider placing intern logic here, to cut down on duplication.
  53         // lambdaForm = findPreexistingEquivalent(lambdaForm)
  54         return new LambdaFormEditor(lambdaForm);
  55     }
  56 
  57     /** A description of a cached transform, possibly associated with the result of the transform.
  58      *  The logical content is a sequence of byte values, starting with a Kind.ordinal value.
  59      *  The sequence is unterminated, ending with an indefinite number of zero bytes.
  60      *  Sequences that are simple (short enough and with small enough values) pack into a 64-bit long.
  61      */
  62     private static final class Transform extends SoftReference<LambdaForm> {
  63         final long packedBytes;
  64         final byte[] fullBytes;
  65 
  66         private enum Kind {
  67             NO_KIND,  // necessary because ordinal must be greater than zero
  68             BIND_ARG, ADD_ARG, DUP_ARG,
  69             SPREAD_ARGS,
  70             FILTER_ARG, FILTER_RETURN, FILTER_RETURN_TO_ZERO,
  71             COLLECT_ARGS, COLLECT_ARGS_TO_VOID, COLLECT_ARGS_TO_ARRAY,
  72             FOLD_ARGS, FOLD_ARGS_TO_VOID,
  73             PERMUTE_ARGS
  74             //maybe add more for guard with test, catch exception, pointwise type conversions
  75         }
  76 
  77         private static final boolean STRESS_TEST = false; // turn on to disable most packing
  78         private static final int
  79                 PACKED_BYTE_SIZE = (STRESS_TEST ? 2 : 4),
  80                 PACKED_BYTE_MASK = (1 << PACKED_BYTE_SIZE) - 1,
  81                 PACKED_BYTE_MAX_LENGTH = (STRESS_TEST ? 3 : 64 / PACKED_BYTE_SIZE);
  82 
  83         private static long packedBytes(byte[] bytes) {
  84             if (bytes.length > PACKED_BYTE_MAX_LENGTH)  return 0;
  85             long pb = 0;
  86             int bitset = 0;
  87             for (int i = 0; i < bytes.length; i++) {
  88                 int b = bytes[i] & 0xFF;
  89                 bitset |= b;
  90                 pb |= (long)b << (i * PACKED_BYTE_SIZE);
  91             }
  92             if (!inRange(bitset))
  93                 return 0;
  94             return pb;
  95         }
  96         private static long packedBytes(int b0, int b1) {
  97             assert(inRange(b0 | b1));
  98             return (  (b0 << 0*PACKED_BYTE_SIZE)
  99                     | (b1 << 1*PACKED_BYTE_SIZE));
 100         }
 101         private static long packedBytes(int b0, int b1, int b2) {
 102             assert(inRange(b0 | b1 | b2));
 103             return (  (b0 << 0*PACKED_BYTE_SIZE)
 104                     | (b1 << 1*PACKED_BYTE_SIZE)
 105                     | (b2 << 2*PACKED_BYTE_SIZE));
 106         }
 107         private static long packedBytes(int b0, int b1, int b2, int b3) {
 108             assert(inRange(b0 | b1 | b2 | b3));
 109             return (  (b0 << 0*PACKED_BYTE_SIZE)
 110                     | (b1 << 1*PACKED_BYTE_SIZE)
 111                     | (b2 << 2*PACKED_BYTE_SIZE)
 112                     | (b3 << 3*PACKED_BYTE_SIZE));
 113         }
 114         private static boolean inRange(int bitset) {
 115             assert((bitset & 0xFF) == bitset);  // incoming values must fit in *unsigned* byte
 116             return ((bitset & ~PACKED_BYTE_MASK) == 0);
 117         }
 118         private static byte[] fullBytes(int... byteValues) {
 119             byte[] bytes = new byte[byteValues.length];
 120             int i = 0;
 121             for (int bv : byteValues) {
 122                 bytes[i++] = bval(bv);
 123             }
 124             assert(packedBytes(bytes) == 0);
 125             return bytes;
 126         }
 127 
 128         private byte byteAt(int i) {
 129             long pb = packedBytes;
 130             if (pb == 0) {
 131                 if (i >= fullBytes.length)  return 0;
 132                 return fullBytes[i];
 133             }
 134             assert(fullBytes == null);
 135             if (i > PACKED_BYTE_MAX_LENGTH)  return 0;
 136             int pos = (i * PACKED_BYTE_SIZE);
 137             return (byte)((pb >>> pos) & PACKED_BYTE_MASK);
 138         }
 139 
 140         Kind kind() { return Kind.values()[byteAt(0)]; }
 141 
 142         private Transform(long packedBytes, byte[] fullBytes, LambdaForm result) {
 143             super(result);
 144             this.packedBytes = packedBytes;
 145             this.fullBytes = fullBytes;
 146         }
 147         private Transform(long packedBytes) {
 148             this(packedBytes, null, null);
 149             assert(packedBytes != 0);
 150         }
 151         private Transform(byte[] fullBytes) {
 152             this(0, fullBytes, null);
 153         }
 154 
 155         private static byte bval(int b) {
 156             assert((b & 0xFF) == b);  // incoming value must fit in *unsigned* byte
 157             return (byte)b;
 158         }
 159         private static byte bval(Kind k) {
 160             return bval(k.ordinal());
 161         }
 162         static Transform of(Kind k, int b1) {
 163             byte b0 = bval(k);
 164             if (inRange(b0 | b1))
 165                 return new Transform(packedBytes(b0, b1));
 166             else
 167                 return new Transform(fullBytes(b0, b1));
 168         }
 169         static Transform of(Kind k, int b1, int b2) {
 170             byte b0 = (byte) k.ordinal();
 171             if (inRange(b0 | b1 | b2))
 172                 return new Transform(packedBytes(b0, b1, b2));
 173             else
 174                 return new Transform(fullBytes(b0, b1, b2));
 175         }
 176         static Transform of(Kind k, int b1, int b2, int b3) {
 177             byte b0 = (byte) k.ordinal();
 178             if (inRange(b0 | b1 | b2 | b3))
 179                 return new Transform(packedBytes(b0, b1, b2, b3));
 180             else
 181                 return new Transform(fullBytes(b0, b1, b2, b3));
 182         }
 183         private static final byte[] NO_BYTES = {};
 184         static Transform of(Kind k, int... b123) {
 185             return ofBothArrays(k, b123, NO_BYTES);
 186         }
 187         static Transform of(Kind k, int b1, byte[] b234) {
 188             return ofBothArrays(k, new int[]{ b1 }, b234);
 189         }
 190         static Transform of(Kind k, int b1, int b2, byte[] b345) {
 191             return ofBothArrays(k, new int[]{ b1, b2 }, b345);
 192         }
 193         private static Transform ofBothArrays(Kind k, int[] b123, byte[] b456) {
 194             byte[] fullBytes = new byte[1 + b123.length + b456.length];
 195             int i = 0;
 196             fullBytes[i++] = bval(k);
 197             for (int bv : b123) {
 198                 fullBytes[i++] = bval(bv);
 199             }
 200             for (byte bv : b456) {
 201                 fullBytes[i++] = bv;
 202             }
 203             long packedBytes = packedBytes(fullBytes);
 204             if (packedBytes != 0)
 205                 return new Transform(packedBytes);
 206             else
 207                 return new Transform(fullBytes);
 208         }
 209 
 210         Transform withResult(LambdaForm result) {
 211             return new Transform(this.packedBytes, this.fullBytes, result);
 212         }
 213 
 214         @Override
 215         public boolean equals(Object obj) {
 216             return obj instanceof Transform && equals((Transform)obj);
 217         }
 218         public boolean equals(Transform that) {
 219             return this.packedBytes == that.packedBytes && Arrays.equals(this.fullBytes, that.fullBytes);
 220         }
 221         @Override
 222         public int hashCode() {
 223             if (packedBytes != 0) {
 224                 assert(fullBytes == null);
 225                 return Long.hashCode(packedBytes);
 226             }
 227             return Arrays.hashCode(fullBytes);
 228         }
 229         @Override
 230         public String toString() {
 231             StringBuilder buf = new StringBuilder();
 232             long bits = packedBytes;
 233             if (bits != 0) {
 234                 buf.append("(");
 235                 while (bits != 0) {
 236                     buf.append(bits & PACKED_BYTE_MASK);
 237                     bits >>>= PACKED_BYTE_SIZE;
 238                     if (bits != 0)  buf.append(",");
 239                 }
 240                 buf.append(")");
 241             }
 242             if (fullBytes != null) {
 243                 buf.append("unpacked");
 244                 buf.append(Arrays.toString(fullBytes));
 245             }
 246             LambdaForm result = get();
 247             if (result != null) {
 248                 buf.append(" result=");
 249                 buf.append(result);
 250             }
 251             return buf.toString();
 252         }
 253     }
 254 
 255     /** Find a previously cached transform equivalent to the given one, and return its result. */
 256     private LambdaForm getInCache(Transform key) {
 257         assert(key.get() == null);
 258         // The transformCache is one of null, Transform, Transform[], or ConcurrentHashMap.
 259         Object c = lambdaForm.transformCache;
 260         Transform k = null;
 261         if (c instanceof ConcurrentHashMap) {
 262             @SuppressWarnings("unchecked")
 263             ConcurrentHashMap<Transform,Transform> m = (ConcurrentHashMap<Transform,Transform>) c;
 264             k = m.get(key);
 265         } else if (c == null) {
 266             return null;
 267         } else if (c instanceof Transform) {
 268             // one-element cache avoids overhead of an array
 269             Transform t = (Transform)c;
 270             if (t.equals(key))  k = t;
 271         } else {
 272             Transform[] ta = (Transform[])c;
 273             for (int i = 0; i < ta.length; i++) {
 274                 Transform t = ta[i];
 275                 if (t == null)  break;
 276                 if (t.equals(key)) { k = t; break; }
 277             }
 278         }
 279         assert(k == null || key.equals(k));
 280         return (k != null) ? k.get() : null;
 281     }
 282 
 283     /** Arbitrary but reasonable limits on Transform[] size for cache. */
 284     private static final int MIN_CACHE_ARRAY_SIZE = 4, MAX_CACHE_ARRAY_SIZE = 16;
 285 
 286     /** Cache a transform with its result, and return that result.
 287      *  But if an equivalent transform has already been cached, return its result instead.
 288      */
 289     private LambdaForm putInCache(Transform key, LambdaForm form) {
 290         key = key.withResult(form);
 291         for (int pass = 0; ; pass++) {
 292             Object c = lambdaForm.transformCache;
 293             if (c instanceof ConcurrentHashMap) {
 294                 @SuppressWarnings("unchecked")
 295                 ConcurrentHashMap<Transform,Transform> m = (ConcurrentHashMap<Transform,Transform>) c;
 296                 Transform k = m.putIfAbsent(key, key);
 297                 if (k == null) return form;
 298                 LambdaForm result = k.get();
 299                 if (result != null) {
 300                     return result;
 301                 } else {
 302                     if (m.replace(key, k, key)) {
 303                         return form;
 304                     } else {
 305                         continue;
 306                     }
 307                 }
 308             }
 309             assert(pass == 0);
 310             synchronized (lambdaForm) {
 311                 c = lambdaForm.transformCache;
 312                 if (c instanceof ConcurrentHashMap)
 313                     continue;
 314                 if (c == null) {
 315                     lambdaForm.transformCache = key;
 316                     return form;
 317                 }
 318                 Transform[] ta;
 319                 if (c instanceof Transform) {
 320                     Transform k = (Transform)c;
 321                     if (k.equals(key)) {
 322                         LambdaForm result = k.get();
 323                         if (result == null) {
 324                             lambdaForm.transformCache = key;
 325                             return form;
 326                         } else {
 327                             return result;
 328                         }
 329                     } else if (k.get() == null) { // overwrite stale entry
 330                         lambdaForm.transformCache = key;
 331                         return form;
 332                     }
 333                     // expand one-element cache to small array
 334                     ta = new Transform[MIN_CACHE_ARRAY_SIZE];
 335                     ta[0] = k;
 336                     lambdaForm.transformCache = ta;
 337                 } else {
 338                     // it is already expanded
 339                     ta = (Transform[])c;
 340                 }
 341                 int len = ta.length;
 342                 int stale = -1;
 343                 int i;
 344                 for (i = 0; i < len; i++) {
 345                     Transform k = ta[i];
 346                     if (k == null) {
 347                         break;
 348                     }
 349                     if (k.equals(key)) {
 350                         LambdaForm result = k.get();
 351                         if (result == null) {
 352                             ta[i] = key;
 353                             return form;
 354                         } else {
 355                             return result;
 356                         }
 357                     } else if (stale < 0 && k.get() == null) {
 358                         stale = i; // remember 1st stale entry index
 359                     }
 360                 }
 361                 if (i < len || stale >= 0) {
 362                     // just fall through to cache update
 363                 } else if (len < MAX_CACHE_ARRAY_SIZE) {
 364                     len = Math.min(len * 2, MAX_CACHE_ARRAY_SIZE);
 365                     ta = Arrays.copyOf(ta, len);
 366                     lambdaForm.transformCache = ta;
 367                 } else {
 368                     ConcurrentHashMap<Transform, Transform> m = new ConcurrentHashMap<>(MAX_CACHE_ARRAY_SIZE * 2);
 369                     for (Transform k : ta) {
 370                         m.put(k, k);
 371                     }
 372                     lambdaForm.transformCache = m;
 373                     // The second iteration will update for this query, concurrently.
 374                     continue;
 375                 }
 376                 int idx = (stale >= 0) ? stale : i;
 377                 ta[idx] = key;
 378                 return form;
 379             }
 380         }
 381     }
 382 
 383     private LambdaFormBuffer buffer() {
 384         return new LambdaFormBuffer(lambdaForm);
 385     }
 386 
 387     /// Editing methods for method handles.  These need to have fast paths.
 388 
 389     private BoundMethodHandle.SpeciesData oldSpeciesData() {
 390         return BoundMethodHandle.speciesData(lambdaForm);
 391     }
 392     private BoundMethodHandle.SpeciesData newSpeciesData(BasicType type) {
 393         return oldSpeciesData().extendWith(type);
 394     }
 395 
 396     BoundMethodHandle bindArgumentL(BoundMethodHandle mh, int pos, Object value) {
 397         assert(mh.speciesData() == oldSpeciesData());
 398         BasicType bt = L_TYPE;
 399         MethodType type2 = bindArgumentType(mh, pos, bt);
 400         LambdaForm form2 = bindArgumentForm(1+pos);
 401         return mh.copyWithExtendL(type2, form2, value);
 402     }
 403     BoundMethodHandle bindArgumentI(BoundMethodHandle mh, int pos, int value) {
 404         assert(mh.speciesData() == oldSpeciesData());
 405         BasicType bt = I_TYPE;
 406         MethodType type2 = bindArgumentType(mh, pos, bt);
 407         LambdaForm form2 = bindArgumentForm(1+pos);
 408         return mh.copyWithExtendI(type2, form2, value);
 409     }
 410 
 411     BoundMethodHandle bindArgumentJ(BoundMethodHandle mh, int pos, long value) {
 412         assert(mh.speciesData() == oldSpeciesData());
 413         BasicType bt = J_TYPE;
 414         MethodType type2 = bindArgumentType(mh, pos, bt);
 415         LambdaForm form2 = bindArgumentForm(1+pos);
 416         return mh.copyWithExtendJ(type2, form2, value);
 417     }
 418 
 419     BoundMethodHandle bindArgumentF(BoundMethodHandle mh, int pos, float value) {
 420         assert(mh.speciesData() == oldSpeciesData());
 421         BasicType bt = F_TYPE;
 422         MethodType type2 = bindArgumentType(mh, pos, bt);
 423         LambdaForm form2 = bindArgumentForm(1+pos);
 424         return mh.copyWithExtendF(type2, form2, value);
 425     }
 426 
 427     BoundMethodHandle bindArgumentD(BoundMethodHandle mh, int pos, double value) {
 428         assert(mh.speciesData() == oldSpeciesData());
 429         BasicType bt = D_TYPE;
 430         MethodType type2 = bindArgumentType(mh, pos, bt);
 431         LambdaForm form2 = bindArgumentForm(1+pos);
 432         return mh.copyWithExtendD(type2, form2, value);
 433     }
 434 
 435     private MethodType bindArgumentType(BoundMethodHandle mh, int pos, BasicType bt) {
 436         assert(mh.form == lambdaForm);
 437         assert(mh.form.names[1+pos].type == bt);
 438         assert(BasicType.basicType(mh.type().parameterType(pos)) == bt);
 439         return mh.type().dropParameterTypes(pos, pos+1);
 440     }
 441 
 442     /// Editing methods for lambda forms.
 443     // Each editing method can (potentially) cache the edited LF so that it can be reused later.
 444 
 445     LambdaForm bindArgumentForm(int pos) {
 446         Transform key = Transform.of(Transform.Kind.BIND_ARG, pos);
 447         LambdaForm form = getInCache(key);
 448         if (form != null) {
 449             assert(form.parameterConstraint(0) == newSpeciesData(lambdaForm.parameterType(pos)));
 450             return form;
 451         }
 452         LambdaFormBuffer buf = buffer();
 453         buf.startEdit();
 454 
 455         BoundMethodHandle.SpeciesData oldData = oldSpeciesData();
 456         BoundMethodHandle.SpeciesData newData = newSpeciesData(lambdaForm.parameterType(pos));
 457         Name oldBaseAddress = lambdaForm.parameter(0);  // BMH holding the values
 458         Name newBaseAddress;
 459         NamedFunction getter = newData.getterFunction(oldData.fieldCount());
 460 
 461         if (pos != 0) {
 462             // The newly created LF will run with a different BMH.
 463             // Switch over any pre-existing BMH field references to the new BMH class.
 464             buf.replaceFunctions(oldData.getterFunctions(), newData.getterFunctions(), oldBaseAddress);
 465             newBaseAddress = oldBaseAddress.withConstraint(newData);
 466             buf.renameParameter(0, newBaseAddress);
 467             buf.replaceParameterByNewExpression(pos, new Name(getter, newBaseAddress));
 468         } else {
 469             // cannot bind the MH arg itself, unless oldData is empty
 470             assert(oldData == BoundMethodHandle.SpeciesData.EMPTY);
 471             newBaseAddress = new Name(L_TYPE).withConstraint(newData);
 472             buf.replaceParameterByNewExpression(0, new Name(getter, newBaseAddress));
 473             buf.insertParameter(0, newBaseAddress);
 474         }
 475 
 476         form = buf.endEdit();
 477         return putInCache(key, form);
 478     }
 479 
 480     LambdaForm addArgumentForm(int pos, BasicType type) {
 481         Transform key = Transform.of(Transform.Kind.ADD_ARG, pos, type.ordinal());
 482         LambdaForm form = getInCache(key);
 483         if (form != null) {
 484             assert(form.arity == lambdaForm.arity+1);
 485             assert(form.parameterType(pos) == type);
 486             return form;
 487         }
 488         LambdaFormBuffer buf = buffer();
 489         buf.startEdit();
 490 
 491         buf.insertParameter(pos, new Name(type));
 492 
 493         form = buf.endEdit();
 494         return putInCache(key, form);
 495     }
 496 
 497     LambdaForm dupArgumentForm(int srcPos, int dstPos) {
 498         Transform key = Transform.of(Transform.Kind.DUP_ARG, srcPos, dstPos);
 499         LambdaForm form = getInCache(key);
 500         if (form != null) {
 501             assert(form.arity == lambdaForm.arity-1);
 502             return form;
 503         }
 504         LambdaFormBuffer buf = buffer();
 505         buf.startEdit();
 506 
 507         assert(lambdaForm.parameter(srcPos).constraint == null);
 508         assert(lambdaForm.parameter(dstPos).constraint == null);
 509         buf.replaceParameterByCopy(dstPos, srcPos);
 510 
 511         form = buf.endEdit();
 512         return putInCache(key, form);
 513     }
 514 
 515     LambdaForm spreadArgumentsForm(int pos, Class<?> arrayType, int arrayLength) {
 516         Class<?> elementType = arrayType.getComponentType();
 517         Class<?> erasedArrayType = arrayType;
 518         if (!elementType.isPrimitive())
 519             erasedArrayType = Object[].class;
 520         BasicType bt = basicType(elementType);
 521         int elementTypeKey = bt.ordinal();
 522         if (bt.basicTypeClass() != elementType) {
 523             if (elementType.isPrimitive()) {
 524                 elementTypeKey = TYPE_LIMIT + Wrapper.forPrimitiveType(elementType).ordinal();
 525             }
 526         }
 527         Transform key = Transform.of(Transform.Kind.SPREAD_ARGS, pos, elementTypeKey, arrayLength);
 528         LambdaForm form = getInCache(key);
 529         if (form != null) {
 530             assert(form.arity == lambdaForm.arity - arrayLength + 1);
 531             return form;
 532         }
 533         LambdaFormBuffer buf = buffer();
 534         buf.startEdit();
 535 
 536         assert(pos <= MethodType.MAX_JVM_ARITY);
 537         assert(pos + arrayLength <= lambdaForm.arity);
 538         assert(pos > 0);  // cannot spread the MH arg itself
 539 
 540         Name spreadParam = new Name(L_TYPE);
 541         Name checkSpread = new Name(MethodHandleImpl.Lazy.NF_checkSpreadArgument, spreadParam, arrayLength);
 542 
 543         // insert the new expressions
 544         int exprPos = lambdaForm.arity();
 545         buf.insertExpression(exprPos++, checkSpread);
 546         // adjust the arguments
 547         MethodHandle aload = MethodHandles.arrayElementGetter(erasedArrayType);
 548         for (int i = 0; i < arrayLength; i++) {
 549             Name loadArgument = new Name(aload, spreadParam, i);
 550             buf.insertExpression(exprPos + i, loadArgument);
 551             buf.replaceParameterByCopy(pos + i, exprPos + i);
 552         }
 553         buf.insertParameter(pos, spreadParam);
 554 
 555         form = buf.endEdit();
 556         return putInCache(key, form);
 557     }
 558 
 559     LambdaForm collectArgumentsForm(int pos, MethodType collectorType) {
 560         int collectorArity = collectorType.parameterCount();
 561         boolean dropResult = (collectorType.returnType() == void.class);
 562         if (collectorArity == 1 && !dropResult) {
 563             return filterArgumentForm(pos, basicType(collectorType.parameterType(0)));
 564         }
 565         BasicType[] newTypes = BasicType.basicTypes(collectorType.parameterList());
 566         Transform.Kind kind = (dropResult
 567                 ? Transform.Kind.COLLECT_ARGS_TO_VOID
 568                 : Transform.Kind.COLLECT_ARGS);
 569         if (dropResult && collectorArity == 0)  pos = 1;  // pure side effect
 570         Transform key = Transform.of(kind, pos, collectorArity, BasicType.basicTypesOrd(newTypes));
 571         LambdaForm form = getInCache(key);
 572         if (form != null) {
 573             assert(form.arity == lambdaForm.arity - (dropResult ? 0 : 1) + collectorArity);
 574             return form;
 575         }
 576         form = makeArgumentCombinationForm(pos, collectorType, false, dropResult);
 577         return putInCache(key, form);
 578     }
 579 
 580     LambdaForm collectArgumentArrayForm(int pos, MethodHandle arrayCollector) {
 581         MethodType collectorType = arrayCollector.type();
 582         int collectorArity = collectorType.parameterCount();
 583         assert(arrayCollector.intrinsicName() == Intrinsic.NEW_ARRAY);
 584         Class<?> arrayType = collectorType.returnType();
 585         Class<?> elementType = arrayType.getComponentType();
 586         BasicType argType = basicType(elementType);
 587         int argTypeKey = argType.ordinal();
 588         if (argType.basicTypeClass() != elementType) {
 589             // return null if it requires more metadata (like String[].class)
 590             if (!elementType.isPrimitive())
 591                 return null;
 592             argTypeKey = TYPE_LIMIT + Wrapper.forPrimitiveType(elementType).ordinal();
 593         }
 594         assert(collectorType.parameterList().equals(Collections.nCopies(collectorArity, elementType)));
 595         Transform.Kind kind = Transform.Kind.COLLECT_ARGS_TO_ARRAY;
 596         Transform key = Transform.of(kind, pos, collectorArity, argTypeKey);
 597         LambdaForm form = getInCache(key);
 598         if (form != null) {
 599             assert(form.arity == lambdaForm.arity - 1 + collectorArity);
 600             return form;
 601         }
 602         LambdaFormBuffer buf = buffer();
 603         buf.startEdit();
 604 
 605         assert(pos + 1 <= lambdaForm.arity);
 606         assert(pos > 0);  // cannot filter the MH arg itself
 607 
 608         Name[] newParams = new Name[collectorArity];
 609         for (int i = 0; i < collectorArity; i++) {
 610             newParams[i] = new Name(pos + i, argType);
 611         }
 612         Name callCombiner = new Name(arrayCollector, (Object[]) /*...*/ newParams);
 613 
 614         // insert the new expression
 615         int exprPos = lambdaForm.arity();
 616         buf.insertExpression(exprPos, callCombiner);
 617 
 618         // insert new arguments
 619         int argPos = pos + 1;  // skip result parameter
 620         for (Name newParam : newParams) {
 621             buf.insertParameter(argPos++, newParam);
 622         }
 623         assert(buf.lastIndexOf(callCombiner) == exprPos+newParams.length);
 624         buf.replaceParameterByCopy(pos, exprPos+newParams.length);
 625 
 626         form = buf.endEdit();
 627         return putInCache(key, form);
 628     }
 629 
 630     LambdaForm filterArgumentForm(int pos, BasicType newType) {
 631         Transform key = Transform.of(Transform.Kind.FILTER_ARG, pos, newType.ordinal());
 632         LambdaForm form = getInCache(key);
 633         if (form != null) {
 634             assert(form.arity == lambdaForm.arity);
 635             assert(form.parameterType(pos) == newType);
 636             return form;
 637         }
 638 
 639         BasicType oldType = lambdaForm.parameterType(pos);
 640         MethodType filterType = MethodType.methodType(oldType.basicTypeClass(),
 641                 newType.basicTypeClass());
 642         form = makeArgumentCombinationForm(pos, filterType, false, false);
 643         return putInCache(key, form);
 644     }
 645 
 646     private LambdaForm makeArgumentCombinationForm(int pos,
 647                                                    MethodType combinerType,
 648                                                    boolean keepArguments, boolean dropResult) {
 649         LambdaFormBuffer buf = buffer();
 650         buf.startEdit();
 651         int combinerArity = combinerType.parameterCount();
 652         int resultArity = (dropResult ? 0 : 1);
 653 
 654         assert(pos <= MethodType.MAX_JVM_ARITY);
 655         assert(pos + resultArity + (keepArguments ? combinerArity : 0) <= lambdaForm.arity);
 656         assert(pos > 0);  // cannot filter the MH arg itself
 657         assert(combinerType == combinerType.basicType());
 658         assert(combinerType.returnType() != void.class || dropResult);
 659 
 660         BoundMethodHandle.SpeciesData oldData = oldSpeciesData();
 661         BoundMethodHandle.SpeciesData newData = newSpeciesData(L_TYPE);
 662 
 663         // The newly created LF will run with a different BMH.
 664         // Switch over any pre-existing BMH field references to the new BMH class.
 665         Name oldBaseAddress = lambdaForm.parameter(0);  // BMH holding the values
 666         buf.replaceFunctions(oldData.getterFunctions(), newData.getterFunctions(), oldBaseAddress);
 667         Name newBaseAddress = oldBaseAddress.withConstraint(newData);
 668         buf.renameParameter(0, newBaseAddress);
 669 
 670         Name getCombiner = new Name(newData.getterFunction(oldData.fieldCount()), newBaseAddress);
 671         Object[] combinerArgs = new Object[1 + combinerArity];
 672         combinerArgs[0] = getCombiner;
 673         Name[] newParams;
 674         if (keepArguments) {
 675             newParams = new Name[0];
 676             System.arraycopy(lambdaForm.names, pos + resultArity,
 677                              combinerArgs, 1, combinerArity);
 678         } else {
 679             newParams = new Name[combinerArity];
 680             BasicType[] newTypes = basicTypes(combinerType.parameterList());
 681             for (int i = 0; i < newTypes.length; i++) {
 682                 newParams[i] = new Name(pos + i, newTypes[i]);
 683             }
 684             System.arraycopy(newParams, 0,
 685                              combinerArgs, 1, combinerArity);
 686         }
 687         Name callCombiner = new Name(combinerType, combinerArgs);
 688 
 689         // insert the two new expressions
 690         int exprPos = lambdaForm.arity();
 691         buf.insertExpression(exprPos+0, getCombiner);
 692         buf.insertExpression(exprPos+1, callCombiner);
 693 
 694         // insert new arguments, if needed
 695         int argPos = pos + resultArity;  // skip result parameter
 696         for (Name newParam : newParams) {
 697             buf.insertParameter(argPos++, newParam);
 698         }
 699         assert(buf.lastIndexOf(callCombiner) == exprPos+1+newParams.length);
 700         if (!dropResult) {
 701             buf.replaceParameterByCopy(pos, exprPos+1+newParams.length);
 702         }
 703 
 704         return buf.endEdit();
 705     }
 706 
 707     LambdaForm filterReturnForm(BasicType newType, boolean constantZero) {
 708         Transform.Kind kind = (constantZero ? Transform.Kind.FILTER_RETURN_TO_ZERO : Transform.Kind.FILTER_RETURN);
 709         Transform key = Transform.of(kind, newType.ordinal());
 710         LambdaForm form = getInCache(key);
 711         if (form != null) {
 712             assert(form.arity == lambdaForm.arity);
 713             assert(form.returnType() == newType);
 714             return form;
 715         }
 716         LambdaFormBuffer buf = buffer();
 717         buf.startEdit();
 718 
 719         int insPos = lambdaForm.names.length;
 720         Name callFilter;
 721         if (constantZero) {
 722             // Synthesize a constant zero value for the given type.
 723             if (newType == V_TYPE)
 724                 callFilter = null;
 725             else
 726                 callFilter = new Name(constantZero(newType));
 727         } else {
 728             BoundMethodHandle.SpeciesData oldData = oldSpeciesData();
 729             BoundMethodHandle.SpeciesData newData = newSpeciesData(L_TYPE);
 730 
 731             // The newly created LF will run with a different BMH.
 732             // Switch over any pre-existing BMH field references to the new BMH class.
 733             Name oldBaseAddress = lambdaForm.parameter(0);  // BMH holding the values
 734             buf.replaceFunctions(oldData.getterFunctions(), newData.getterFunctions(), oldBaseAddress);
 735             Name newBaseAddress = oldBaseAddress.withConstraint(newData);
 736             buf.renameParameter(0, newBaseAddress);
 737 
 738             Name getFilter = new Name(newData.getterFunction(oldData.fieldCount()), newBaseAddress);
 739             buf.insertExpression(insPos++, getFilter);
 740             BasicType oldType = lambdaForm.returnType();
 741             if (oldType == V_TYPE) {
 742                 MethodType filterType = MethodType.methodType(newType.basicTypeClass());
 743                 callFilter = new Name(filterType, getFilter);
 744             } else {
 745                 MethodType filterType = MethodType.methodType(newType.basicTypeClass(), oldType.basicTypeClass());
 746                 callFilter = new Name(filterType, getFilter, lambdaForm.names[lambdaForm.result]);
 747             }
 748         }
 749 
 750         if (callFilter != null)
 751             buf.insertExpression(insPos++, callFilter);
 752         buf.setResult(callFilter);
 753 
 754         form = buf.endEdit();
 755         return putInCache(key, form);
 756     }
 757 
 758     LambdaForm foldArgumentsForm(int foldPos, boolean dropResult, MethodType combinerType) {
 759         int combinerArity = combinerType.parameterCount();
 760         Transform.Kind kind = (dropResult ? Transform.Kind.FOLD_ARGS_TO_VOID : Transform.Kind.FOLD_ARGS);
 761         Transform key = Transform.of(kind, foldPos, combinerArity);
 762         LambdaForm form = getInCache(key);
 763         if (form != null) {
 764             assert(form.arity == lambdaForm.arity - (kind == Transform.Kind.FOLD_ARGS ? 1 : 0));
 765             return form;
 766         }
 767         form = makeArgumentCombinationForm(foldPos, combinerType, true, dropResult);
 768         return putInCache(key, form);
 769     }
 770 
 771     LambdaForm permuteArgumentsForm(int skip, int[] reorder) {
 772         assert(skip == 1);  // skip only the leading MH argument, names[0]
 773         int length = lambdaForm.names.length;
 774         int outArgs = reorder.length;
 775         int inTypes = 0;
 776         boolean nullPerm = true;
 777         for (int i = 0; i < reorder.length; i++) {
 778             int inArg = reorder[i];
 779             if (inArg != i)  nullPerm = false;
 780             inTypes = Math.max(inTypes, inArg+1);
 781         }
 782         assert(skip + reorder.length == lambdaForm.arity);
 783         if (nullPerm)  return lambdaForm;  // do not bother to cache
 784         Transform key = Transform.of(Transform.Kind.PERMUTE_ARGS, reorder);
 785         LambdaForm form = getInCache(key);
 786         if (form != null) {
 787             assert(form.arity == skip+inTypes) : form;
 788             return form;
 789         }
 790 
 791         BasicType[] types = new BasicType[inTypes];
 792         for (int i = 0; i < outArgs; i++) {
 793             int inArg = reorder[i];
 794             types[inArg] = lambdaForm.names[skip + i].type;
 795         }
 796         assert (skip + outArgs == lambdaForm.arity);
 797         assert (permutedTypesMatch(reorder, types, lambdaForm.names, skip));
 798         int pos = 0;
 799         while (pos < outArgs && reorder[pos] == pos) {
 800             pos += 1;
 801         }
 802         Name[] names2 = new Name[length - outArgs + inTypes];
 803         System.arraycopy(lambdaForm.names, 0, names2, 0, skip + pos);
 804         int bodyLength = length - lambdaForm.arity;
 805         System.arraycopy(lambdaForm.names, skip + outArgs, names2, skip + inTypes, bodyLength);
 806         int arity2 = names2.length - bodyLength;
 807         int result2 = lambdaForm.result;
 808         if (result2 >= 0) {
 809             if (result2 < skip + outArgs) {
 810                 result2 = reorder[result2 - skip];
 811             } else {
 812                 result2 = result2 - outArgs + inTypes;
 813             }
 814         }
 815         for (int j = pos; j < outArgs; j++) {
 816             Name n = lambdaForm.names[skip + j];
 817             int i = reorder[j];
 818             Name n2 = names2[skip + i];
 819             if (n2 == null) {
 820                 names2[skip + i] = n2 = new Name(types[i]);
 821             } else {
 822                 assert (n2.type == types[i]);
 823             }
 824             for (int k = arity2; k < names2.length; k++) {
 825                 names2[k] = names2[k].replaceName(n, n2);
 826             }
 827         }
 828         for (int i = skip + pos; i < arity2; i++) {
 829             if (names2[i] == null) {
 830                 names2[i] = argument(i, types[i - skip]);
 831             }
 832         }
 833         for (int j = lambdaForm.arity; j < lambdaForm.names.length; j++) {
 834             int i = j - lambdaForm.arity + arity2;
 835             Name n = lambdaForm.names[j];
 836             Name n2 = names2[i];
 837             if (n != n2) {
 838                 for (int k = i + 1; k < names2.length; k++) {
 839                     names2[k] = names2[k].replaceName(n, n2);
 840                 }
 841             }
 842         }
 843 
 844         form = new LambdaForm(lambdaForm.debugName, arity2, names2, result2);
 845         return putInCache(key, form);
 846     }
 847 
 848     static boolean permutedTypesMatch(int[] reorder, BasicType[] types, Name[] names, int skip) {
 849         for (int i = 0; i < reorder.length; i++) {
 850             assert (names[skip + i].isParam());
 851             assert (names[skip + i].type == types[reorder[i]]);
 852         }
 853         return true;
 854     }
 855 }