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