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