/
niceSOFT
/
openjdk21
Обзор
Документация
Войти
/
niceSOFT
/
openjdk21
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java
1 185 строк
48 KB
Mandy Chung
8301460: Clean up LambdaForm to reference BasicType enums directly
15 фев 2023, 21:29
15 фев 2023, 21:29
50dcc2a
Код
Авторство
О чём код?
/* * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package java.lang.invoke; import sun.invoke.util.Wrapper; import java.lang.ref.SoftReference; import java.util.Arrays; import java.util.Comparator; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import static java.lang.invoke.LambdaForm.*; import static java.lang.invoke.LambdaForm.BasicType.*; import static java.lang.invoke.MethodHandleImpl.Intrinsic; import static java.lang.invoke.MethodHandleImpl.NF_loop; import static java.lang.invoke.MethodHandleImpl.makeIntrinsic; /** Transforms on LFs. * A lambda-form editor can derive new LFs from its base LF. * The editor can cache derived LFs, which simplifies the reuse of their underlying bytecodes. * To support this caching, a LF has an optional pointer to its editor. */ class LambdaFormEditor { final LambdaForm lambdaForm; private LambdaFormEditor(LambdaForm lambdaForm) { this.lambdaForm = lambdaForm; } // Factory method. static LambdaFormEditor lambdaFormEditor(LambdaForm lambdaForm) { // TO DO: Consider placing intern logic here, to cut down on duplication. // lambdaForm = findPreexistingEquivalent(lambdaForm) // Always use uncustomized version for editing. // It helps caching and customized LambdaForms reuse transformCache field to keep a link to uncustomized version. return new LambdaFormEditor(lambdaForm.uncustomize()); } // Transform types // maybe add more for guard with test, catch exception, pointwise type conversions private static final byte BIND_ARG = 1, ADD_ARG = 2, DUP_ARG = 3, SPREAD_ARGS = 4, FILTER_ARG = 5, FILTER_RETURN = 6, COLLECT_ARGS = 7, COLLECT_ARGS_TO_VOID = 8, REPEAT_FILTER_ARGS = 9, FOLD_ARGS = 10, FOLD_ARGS_TO_VOID = 11, PERMUTE_ARGS = 12, LOCAL_TYPES = 13, FILTER_SELECT_ARGS = 14, FOLD_SELECT_ARGS = 15; /** * A description of a cached transform, possibly associated with the result of the transform. * The logical content is a sequence of byte values, starting with a kind value. * The sequence is unterminated, ending with an indefinite number of zero bytes. * Sequences that are simple (short enough and with small enough values) pack into a 64-bit long. * * Tightly coupled with the TransformKey class, which is used to lookup existing * Transforms. */ private static final class Transform extends SoftReference<LambdaForm> { final long packedBytes; final byte[] fullBytes; private Transform(long packedBytes, byte[] fullBytes, LambdaForm result) { super(result); this.packedBytes = packedBytes; this.fullBytes = fullBytes; } @Override public boolean equals(Object obj) { if (obj instanceof TransformKey) { return equals((TransformKey) obj); } return obj instanceof Transform && equals((Transform)obj); } private boolean equals(TransformKey that) { return this.packedBytes == that.packedBytes && Arrays.equals(this.fullBytes, that.fullBytes); } private boolean equals(Transform that) { return this.packedBytes == that.packedBytes && Arrays.equals(this.fullBytes, that.fullBytes); } @Override public int hashCode() { if (packedBytes != 0) { assert(fullBytes == null); return Long.hashCode(packedBytes); } return Arrays.hashCode(fullBytes); } @Override public String toString() { StringBuilder buf = new StringBuilder(); buf.append(new TransformKey(packedBytes, fullBytes).toString()); LambdaForm result = get(); if (result != null) { buf.append(" result="); buf.append(result); } return buf.toString(); } } /** * Used as a lookup key to find existing Transforms */ private static final class TransformKey { final long packedBytes; final byte[] fullBytes; private TransformKey(long packedBytes) { this.packedBytes = packedBytes; this.fullBytes = null; } private TransformKey(byte[] fullBytes) { assert(packedBytes(fullBytes) == 0); this.fullBytes = fullBytes; this.packedBytes = 0; } private TransformKey(long packedBytes, byte[] fullBytes) { assert(fullBytes == null || packedBytes == 0); this.fullBytes = fullBytes; this.packedBytes = packedBytes; } private static byte bval(int b) { assert((b & 0xFF) == b); // incoming value must fit in *unsigned* byte return (byte)b; } private static int ival(int b) { assert((b & 0xFF) == b); // incoming value must fit in *unsigned* byte return b; } static TransformKey of(byte k, int b1) { byte b0 = bval(k); if (inRange(b0 | b1)) return new TransformKey(packedBytes(b0, b1)); else return new TransformKey(fullBytes(b0, b1)); } static TransformKey of(byte b0, int b1, int b2) { if (inRange(b0 | b1 | b2)) return new TransformKey(packedBytes(b0, b1, b2)); else return new TransformKey(fullBytes(b0, b1, b2)); } static TransformKey of(byte b0, int b1, int b2, int b3) { if (inRange(b0 | b1 | b2 | b3)) return new TransformKey(packedBytes(b0, b1, b2, b3)); else return new TransformKey(fullBytes(b0, b1, b2, b3)); } static TransformKey of(byte kind, int... b123) { long packedBytes = packedBytes(kind, b123); if (packedBytes != 0) { return new TransformKey(packedBytes); } byte[] fullBytes = new byte[b123.length + 1]; fullBytes[0] = kind; for (int i = 0; i < b123.length; i++) { fullBytes[i + 1] = TransformKey.bval(b123[i]); } return new TransformKey(fullBytes); } static TransformKey of(byte kind, int b1, int... b234) { long packedBytes = packedBytes(kind, b1, b234); if (packedBytes != 0) { return new TransformKey(packedBytes); } byte[] fullBytes = new byte[b234.length + 2]; fullBytes[0] = kind; fullBytes[1] = bval(b1); for (int i = 0; i < b234.length; i++) { fullBytes[i + 2] = TransformKey.bval(b234[i]); } return new TransformKey(fullBytes); } static TransformKey of(byte kind, int b1, int b2, int... b345) { long packedBytes = packedBytes(kind, b1, b2, b345); if (packedBytes != 0) { return new TransformKey(packedBytes); } byte[] fullBytes = new byte[b345.length + 3]; fullBytes[0] = kind; fullBytes[1] = bval(b1); fullBytes[2] = bval(b2); for (int i = 0; i < b345.length; i++) { fullBytes[i + 3] = TransformKey.bval(b345[i]); } return new TransformKey(fullBytes); } private static final boolean STRESS_TEST = false; // turn on to disable most packing private static final int PACKED_BYTE_SIZE = (STRESS_TEST ? 2 : 4), PACKED_BYTE_MASK = (1 << PACKED_BYTE_SIZE) - 1, PACKED_BYTE_MAX_LENGTH = (STRESS_TEST ? 3 : 64 / PACKED_BYTE_SIZE); private static long packedBytes(byte b0, int b1, int b2, int[] b345) { if (b345.length + 3 > PACKED_BYTE_MAX_LENGTH) return 0; long pb = 0; int bitset = b0 | b1 | b2; for (int i = 0; i < b345.length; i++) { int b = ival(b345[i]); bitset |= b; pb |= (long)b << ((i + 3) * PACKED_BYTE_SIZE); } if (!inRange(bitset)) return 0; pb = pb | packedBytes(b0, b1, b2); return pb; } private static long packedBytes(byte b0, int b1, int[] b234) { if (b234.length + 2 > PACKED_BYTE_MAX_LENGTH) return 0; long pb = 0; int bitset = b0 | b1; for (int i = 0; i < b234.length; i++) { int b = ival(b234[i]); bitset |= b; pb |= (long)b << ((i + 2) * PACKED_BYTE_SIZE); } if (!inRange(bitset)) return 0; pb = pb | packedBytes(b0, b1); return pb; } private static long packedBytes(byte b0, int[] b123) { if (b123.length + 1 > PACKED_BYTE_MAX_LENGTH) return 0; long pb = 0; int bitset = b0; for (int i = 0; i < b123.length; i++) { int b = ival(b123[i]); bitset |= b; pb |= (long)b << ((i + 1) * PACKED_BYTE_SIZE); } if (!inRange(bitset)) return 0; pb = pb | b0; return pb; } private static long packedBytes(byte[] bytes) { if (!inRange(bytes[0]) || bytes.length > PACKED_BYTE_MAX_LENGTH) return 0; long pb = 0; int bitset = 0; for (int i = 0; i < bytes.length; i++) { int b = bytes[i] & 0xFF; bitset |= b; pb |= (long)b << (i * PACKED_BYTE_SIZE); } if (!inRange(bitset)) return 0; return pb; } private static long packedBytes(int b0, int b1) { assert(inRange(b0 | b1)); return ( (b0) | (b1 << 1*PACKED_BYTE_SIZE)); } private static long packedBytes(int b0, int b1, int b2) { assert(inRange(b0 | b1 | b2)); return ( (b0) | (b1 << 1*PACKED_BYTE_SIZE) | (b2 << 2*PACKED_BYTE_SIZE)); } private static long packedBytes(int b0, int b1, int b2, int b3) { assert(inRange(b0 | b1 | b2 | b3)); return ( (b0) | (b1 << 1*PACKED_BYTE_SIZE) | (b2 << 2*PACKED_BYTE_SIZE) | (b3 << 3*PACKED_BYTE_SIZE)); } private static boolean inRange(int bitset) { assert((bitset & 0xFF) == bitset); // incoming values must fit in *unsigned* byte return ((bitset & ~PACKED_BYTE_MASK) == 0); } private static byte[] fullBytes(int... byteValues) { byte[] bytes = new byte[byteValues.length]; int i = 0; for (int bv : byteValues) { bytes[i++] = bval(bv); } assert(packedBytes(bytes) == 0); return bytes; } Transform withResult(LambdaForm result) { return new Transform(this.packedBytes, this.fullBytes, result); } @Override public String toString() { StringBuilder buf = new StringBuilder(); long bits = packedBytes; if (bits != 0) { buf.append("("); while (bits != 0) { buf.append(bits & PACKED_BYTE_MASK); bits >>>= PACKED_BYTE_SIZE; if (bits != 0) buf.append(","); } buf.append(")"); } if (fullBytes != null) { buf.append("unpacked"); buf.append(Arrays.toString(fullBytes)); } return buf.toString(); } @Override public boolean equals(Object obj) { if (obj instanceof TransformKey) { return equals((TransformKey) obj); } return obj instanceof Transform && equals((Transform)obj); } private boolean equals(TransformKey that) { return this.packedBytes == that.packedBytes && Arrays.equals(this.fullBytes, that.fullBytes); } private boolean equals(Transform that) { return this.packedBytes == that.packedBytes && Arrays.equals(this.fullBytes, that.fullBytes); } @Override public int hashCode() { if (packedBytes != 0) { return Long.hashCode(packedBytes); } return Arrays.hashCode(fullBytes); } } /** Find a previously cached transform equivalent to the given one, and return its result. */ private LambdaForm getInCache(TransformKey key) { // The transformCache is one of null, Transform, Transform[], or ConcurrentHashMap. Object c = lambdaForm.transformCache; Transform k = null; if (c instanceof ConcurrentHashMap) { @SuppressWarnings("unchecked") ConcurrentHashMap<Transform,Transform> m = (ConcurrentHashMap<Transform,Transform>) c; k = m.get(key); } else if (c == null) { return null; } else if (c instanceof Transform t) { // one-element cache avoids overhead of an array if (t.equals(key)) k = t; } else { Transform[] ta = (Transform[])c; for (int i = 0; i < ta.length; i++) { Transform t = ta[i]; if (t == null) break; if (t.equals(key)) { k = t; break; } } } assert(k == null || key.equals(k)); return (k != null) ? k.get() : null; } /** Arbitrary but reasonable limits on Transform[] size for cache. */ private static final int MIN_CACHE_ARRAY_SIZE = 4, MAX_CACHE_ARRAY_SIZE = 16; /** Cache a transform with its result, and return that result. * But if an equivalent transform has already been cached, return its result instead. */ private LambdaForm putInCache(TransformKey key, LambdaForm form) { Transform transform = key.withResult(form); for (int pass = 0; ; pass++) { Object c = lambdaForm.transformCache; if (c instanceof ConcurrentHashMap) { @SuppressWarnings("unchecked") ConcurrentHashMap<Transform,Transform> m = (ConcurrentHashMap<Transform,Transform>) c; Transform k = m.putIfAbsent(transform, transform); if (k == null) return form; LambdaForm result = k.get(); if (result != null) { return result; } else { if (m.replace(transform, k, transform)) { return form; } else { continue; } } } assert(pass == 0); synchronized (lambdaForm) { c = lambdaForm.transformCache; if (c instanceof ConcurrentHashMap) continue; if (c == null) { lambdaForm.transformCache = transform; return form; } Transform[] ta; if (c instanceof Transform k) { if (k.equals(key)) { LambdaForm result = k.get(); if (result == null) { lambdaForm.transformCache = transform; return form; } else { return result; } } else if (k.get() == null) { // overwrite stale entry lambdaForm.transformCache = transform; return form; } // expand one-element cache to small array ta = new Transform[MIN_CACHE_ARRAY_SIZE]; ta[0] = k; lambdaForm.transformCache = ta; } else { // it is already expanded ta = (Transform[])c; } int len = ta.length; int stale = -1; int i; for (i = 0; i < len; i++) { Transform k = ta[i]; if (k == null) { break; } if (k.equals(transform)) { LambdaForm result = k.get(); if (result == null) { ta[i] = transform; return form; } else { return result; } } else if (stale < 0 && k.get() == null) { stale = i; // remember 1st stale entry index } } if (i < len || stale >= 0) { // just fall through to cache update } else if (len < MAX_CACHE_ARRAY_SIZE) { len = Math.min(len * 2, MAX_CACHE_ARRAY_SIZE); ta = Arrays.copyOf(ta, len); lambdaForm.transformCache = ta; } else { ConcurrentHashMap<Transform, Transform> m = new ConcurrentHashMap<>(MAX_CACHE_ARRAY_SIZE * 2); for (Transform k : ta) { m.put(k, k); } lambdaForm.transformCache = m; // The second iteration will update for this query, concurrently. continue; } int idx = (stale >= 0) ? stale : i; ta[idx] = transform; return form; } } } private LambdaFormBuffer buffer() { return new LambdaFormBuffer(lambdaForm); } /// Editing methods for method handles. These need to have fast paths. private BoundMethodHandle.SpeciesData oldSpeciesData() { return BoundMethodHandle.speciesDataFor(lambdaForm); } private BoundMethodHandle.SpeciesData newSpeciesData(BasicType type) { return oldSpeciesData().extendWith(type); } BoundMethodHandle bindArgumentL(BoundMethodHandle mh, int pos, Object value) { assert(mh.speciesData() == oldSpeciesData()); BasicType bt = L_TYPE; MethodType type2 = bindArgumentType(mh, pos, bt); LambdaForm form2 = bindArgumentForm(1+pos); return mh.copyWithExtendL(type2, form2, value); } BoundMethodHandle bindArgumentI(BoundMethodHandle mh, int pos, int value) { assert(mh.speciesData() == oldSpeciesData()); BasicType bt = I_TYPE; MethodType type2 = bindArgumentType(mh, pos, bt); LambdaForm form2 = bindArgumentForm(1+pos); return mh.copyWithExtendI(type2, form2, value); } BoundMethodHandle bindArgumentJ(BoundMethodHandle mh, int pos, long value) { assert(mh.speciesData() == oldSpeciesData()); BasicType bt = J_TYPE; MethodType type2 = bindArgumentType(mh, pos, bt); LambdaForm form2 = bindArgumentForm(1+pos); return mh.copyWithExtendJ(type2, form2, value); } BoundMethodHandle bindArgumentF(BoundMethodHandle mh, int pos, float value) { assert(mh.speciesData() == oldSpeciesData()); BasicType bt = F_TYPE; MethodType type2 = bindArgumentType(mh, pos, bt); LambdaForm form2 = bindArgumentForm(1+pos); return mh.copyWithExtendF(type2, form2, value); } BoundMethodHandle bindArgumentD(BoundMethodHandle mh, int pos, double value) { assert(mh.speciesData() == oldSpeciesData()); BasicType bt = D_TYPE; MethodType type2 = bindArgumentType(mh, pos, bt); LambdaForm form2 = bindArgumentForm(1+pos); return mh.copyWithExtendD(type2, form2, value); } private MethodType bindArgumentType(BoundMethodHandle mh, int pos, BasicType bt) { assert(mh.form.uncustomize() == lambdaForm); assert(mh.form.names[1+pos].type == bt); assert(BasicType.basicType(mh.type().parameterType(pos)) == bt); return mh.type().dropParameterTypes(pos, pos+1); } /// Editing methods for lambda forms. // Each editing method can (potentially) cache the edited LF so that it can be reused later. LambdaForm bindArgumentForm(int pos) { TransformKey key = TransformKey.of(BIND_ARG, pos); LambdaForm form = getInCache(key); if (form != null) { assert(form.parameterConstraint(0) == newSpeciesData(lambdaForm.parameterType(pos))); return form; } LambdaFormBuffer buf = buffer(); buf.startEdit(); BoundMethodHandle.SpeciesData oldData = oldSpeciesData(); BoundMethodHandle.SpeciesData newData = newSpeciesData(lambdaForm.parameterType(pos)); Name oldBaseAddress = lambdaForm.parameter(0); // BMH holding the values Name newBaseAddress; NamedFunction getter = newData.getterFunction(oldData.fieldCount()); if (pos != 0) { // The newly created LF will run with a different BMH. // Switch over any pre-existing BMH field references to the new BMH class. buf.replaceFunctions(oldData.getterFunctions(), newData.getterFunctions(), oldBaseAddress); newBaseAddress = oldBaseAddress.withConstraint(newData); buf.renameParameter(0, newBaseAddress); buf.replaceParameterByNewExpression(pos, new Name(getter, newBaseAddress)); } else { // cannot bind the MH arg itself, unless oldData is empty assert(oldData == BoundMethodHandle.SPECIALIZER.topSpecies()); newBaseAddress = new Name(L_TYPE).withConstraint(newData); buf.replaceParameterByNewExpression(0, new Name(getter, newBaseAddress)); buf.insertParameter(0, newBaseAddress); } form = buf.endEdit(); return putInCache(key, form); } LambdaForm addArgumentForm(int pos, BasicType type) { TransformKey key = TransformKey.of(ADD_ARG, pos, type.ordinal()); LambdaForm form = getInCache(key); if (form != null) { assert(form.arity == lambdaForm.arity+1); assert(form.parameterType(pos) == type); return form; } LambdaFormBuffer buf = buffer(); buf.startEdit(); buf.insertParameter(pos, new Name(type)); form = buf.endEdit(); return putInCache(key, form); } LambdaForm dupArgumentForm(int srcPos, int dstPos) { TransformKey key = TransformKey.of(DUP_ARG, srcPos, dstPos); LambdaForm form = getInCache(key); if (form != null) { assert(form.arity == lambdaForm.arity-1); return form; } LambdaFormBuffer buf = buffer(); buf.startEdit(); assert(lambdaForm.parameter(srcPos).constraint == null); assert(lambdaForm.parameter(dstPos).constraint == null); buf.replaceParameterByCopy(dstPos, srcPos); form = buf.endEdit(); return putInCache(key, form); } LambdaForm spreadArgumentsForm(int pos, Class<?> arrayType, int arrayLength) { Class<?> elementType = arrayType.getComponentType(); Class<?> erasedArrayType = arrayType; if (!elementType.isPrimitive()) erasedArrayType = Object[].class; BasicType bt = basicType(elementType); int elementTypeKey = bt.ordinal(); if (bt.basicTypeClass() != elementType) { if (elementType.isPrimitive()) { elementTypeKey = TYPE_LIMIT + Wrapper.forPrimitiveType(elementType).ordinal(); } } TransformKey key = TransformKey.of(SPREAD_ARGS, pos, elementTypeKey, arrayLength); LambdaForm form = getInCache(key); if (form != null) { assert(form.arity == lambdaForm.arity - arrayLength + 1); return form; } LambdaFormBuffer buf = buffer(); buf.startEdit(); assert(pos <= MethodType.MAX_JVM_ARITY); assert(pos + arrayLength <= lambdaForm.arity); assert(pos > 0); // cannot spread the MH arg itself Name spreadParam = new Name(L_TYPE); Name checkSpread = new Name(MethodHandleImpl.getFunction(MethodHandleImpl.NF_checkSpreadArgument), spreadParam, arrayLength); // insert the new expressions int exprPos = lambdaForm.arity(); buf.insertExpression(exprPos++, checkSpread); // adjust the arguments MethodHandle aload = MethodHandles.arrayElementGetter(erasedArrayType); for (int i = 0; i < arrayLength; i++) { Name loadArgument = new Name(new NamedFunction(makeIntrinsic(aload, Intrinsic.ARRAY_LOAD)), spreadParam, i); buf.insertExpression(exprPos + i, loadArgument); buf.replaceParameterByCopy(pos + i, exprPos + i); } buf.insertParameter(pos, spreadParam); form = buf.endEdit(); return putInCache(key, form); } LambdaForm collectArgumentsForm(int pos, MethodType collectorType) { int collectorArity = collectorType.parameterCount(); boolean dropResult = (collectorType.returnType() == void.class); if (collectorArity == 1 && !dropResult) { return filterArgumentForm(pos, basicType(collectorType.parameterType(0))); } int[] newTypes = BasicType.basicTypesOrd(collectorType.ptypes()); byte kind = (dropResult ? COLLECT_ARGS_TO_VOID : COLLECT_ARGS); if (dropResult && collectorArity == 0) pos = 1; // pure side effect TransformKey key = TransformKey.of(kind, pos, collectorArity, newTypes); LambdaForm form = getInCache(key); if (form != null) { assert(form.arity == lambdaForm.arity - (dropResult ? 0 : 1) + collectorArity); return form; } form = makeArgumentCombinationForm(pos, collectorType, false, dropResult); return putInCache(key, form); } LambdaForm filterArgumentForm(int pos, BasicType newType) { TransformKey key = TransformKey.of(FILTER_ARG, pos, newType.ordinal()); LambdaForm form = getInCache(key); if (form != null) { assert(form.arity == lambdaForm.arity); assert(form.parameterType(pos) == newType); return form; } BasicType oldType = lambdaForm.parameterType(pos); MethodType filterType = MethodType.methodType(oldType.basicTypeClass(), newType.basicTypeClass()); form = makeArgumentCombinationForm(pos, filterType, false, false); return putInCache(key, form); } /** * This creates a LF that will repeatedly invoke some unary filter function * at each of the given positions. This allows fewer LFs and BMH species * classes to be generated in typical cases compared to building up the form * by reapplying of {@code filterArgumentForm(int,BasicType)}, and should do * no worse in the worst case. */ LambdaForm filterRepeatedArgumentForm(BasicType newType, int... argPositions) { assert (argPositions.length > 1); TransformKey key = TransformKey.of(REPEAT_FILTER_ARGS, newType.ordinal(), argPositions); LambdaForm form = getInCache(key); if (form != null) { assert(form.arity == lambdaForm.arity && formParametersMatch(form, newType, argPositions)); return form; } BasicType oldType = lambdaForm.parameterType(argPositions[0]); MethodType filterType = MethodType.methodType(oldType.basicTypeClass(), newType.basicTypeClass()); form = makeRepeatedFilterForm(filterType, argPositions); assert (formParametersMatch(form, newType, argPositions)); return putInCache(key, form); } private boolean formParametersMatch(LambdaForm form, BasicType newType, int... argPositions) { for (int i : argPositions) { if (form.parameterType(i) != newType) { return false; } } return true; } private LambdaForm makeRepeatedFilterForm(MethodType combinerType, int... positions) { assert (combinerType.parameterCount() == 1 && combinerType == combinerType.basicType() && combinerType.returnType() != void.class); LambdaFormBuffer buf = buffer(); buf.startEdit(); BoundMethodHandle.SpeciesData oldData = oldSpeciesData(); BoundMethodHandle.SpeciesData newData = newSpeciesData(L_TYPE); // The newly created LF will run with a different BMH. // Switch over any pre-existing BMH field references to the new BMH class. Name oldBaseAddress = lambdaForm.parameter(0); // BMH holding the values buf.replaceFunctions(oldData.getterFunctions(), newData.getterFunctions(), oldBaseAddress); Name newBaseAddress = oldBaseAddress.withConstraint(newData); buf.renameParameter(0, newBaseAddress); // Insert the new expressions at the end int exprPos = lambdaForm.arity(); Name getCombiner = new Name(newData.getterFunction(oldData.fieldCount()), newBaseAddress); buf.insertExpression(exprPos++, getCombiner); // After inserting expressions, we insert parameters in order // from lowest to highest, simplifying the calculation of where parameters // and expressions are var newParameters = new TreeMap<Name, Integer>(new Comparator<>() { public int compare(Name n1, Name n2) { return n1.index - n2.index; } }); // Insert combiner expressions in reverse order so that the invocation of // the resulting form will invoke the combiners in left-to-right order for (int i = positions.length - 1; i >= 0; --i) { int pos = positions[i]; assert (pos > 0 && pos <= MethodType.MAX_JVM_ARITY && pos < lambdaForm.arity); Name newParameter = new Name(pos, basicType(combinerType.parameterType(0))); Object[] combinerArgs = {getCombiner, newParameter}; Name callCombiner = new Name(combinerType, combinerArgs); buf.insertExpression(exprPos++, callCombiner); newParameters.put(newParameter, exprPos); } // Mix in new parameters from left to right in the buffer (this doesn't change // execution order int offset = 0; for (var entry : newParameters.entrySet()) { Name newParameter = entry.getKey(); int from = entry.getValue(); buf.insertParameter(newParameter.index() + 1 + offset, newParameter); buf.replaceParameterByCopy(newParameter.index() + offset, from + offset); offset++; } return buf.endEdit(); } private LambdaForm makeArgumentCombinationForm(int pos, MethodType combinerType, boolean keepArguments, boolean dropResult) { LambdaFormBuffer buf = buffer(); buf.startEdit(); int combinerArity = combinerType.parameterCount(); int resultArity = (dropResult ? 0 : 1); assert(pos <= MethodType.MAX_JVM_ARITY); assert(pos + resultArity + (keepArguments ? combinerArity : 0) <= lambdaForm.arity); assert(pos > 0); // cannot filter the MH arg itself assert(combinerType == combinerType.basicType()); assert(combinerType.returnType() != void.class || dropResult); BoundMethodHandle.SpeciesData oldData = oldSpeciesData(); BoundMethodHandle.SpeciesData newData = newSpeciesData(L_TYPE); // The newly created LF will run with a different BMH. // Switch over any pre-existing BMH field references to the new BMH class. Name oldBaseAddress = lambdaForm.parameter(0); // BMH holding the values buf.replaceFunctions(oldData.getterFunctions(), newData.getterFunctions(), oldBaseAddress); Name newBaseAddress = oldBaseAddress.withConstraint(newData); buf.renameParameter(0, newBaseAddress); Name getCombiner = new Name(newData.getterFunction(oldData.fieldCount()), newBaseAddress); Object[] combinerArgs = new Object[1 + combinerArity]; combinerArgs[0] = getCombiner; Name[] newParams; if (keepArguments) { newParams = new Name[0]; System.arraycopy(lambdaForm.names, pos + resultArity, combinerArgs, 1, combinerArity); } else { newParams = new Name[combinerArity]; for (int i = 0; i < newParams.length; i++) { newParams[i] = new Name(pos + i, basicType(combinerType.parameterType(i))); } System.arraycopy(newParams, 0, combinerArgs, 1, combinerArity); } Name callCombiner = new Name(combinerType, combinerArgs); // insert the two new expressions int exprPos = lambdaForm.arity(); buf.insertExpression(exprPos+0, getCombiner); buf.insertExpression(exprPos+1, callCombiner); // insert new arguments, if needed int argPos = pos + resultArity; // skip result parameter for (Name newParam : newParams) { buf.insertParameter(argPos++, newParam); } assert(buf.lastIndexOf(callCombiner) == exprPos+1+newParams.length); if (!dropResult) { buf.replaceParameterByCopy(pos, exprPos+1+newParams.length); } return buf.endEdit(); } private LambdaForm makeArgumentCombinationForm(int pos, MethodType combinerType, int[] argPositions, boolean keepArguments, boolean dropResult) { LambdaFormBuffer buf = buffer(); buf.startEdit(); int combinerArity = combinerType.parameterCount(); assert(combinerArity == argPositions.length); int resultArity = (dropResult ? 0 : 1); assert(pos <= lambdaForm.arity); assert(pos > 0); // cannot filter the MH arg itself assert(combinerType == combinerType.basicType()); assert(combinerType.returnType() != void.class || dropResult); BoundMethodHandle.SpeciesData oldData = oldSpeciesData(); BoundMethodHandle.SpeciesData newData = newSpeciesData(L_TYPE); // The newly created LF will run with a different BMH. // Switch over any pre-existing BMH field references to the new BMH class. Name oldBaseAddress = lambdaForm.parameter(0); // BMH holding the values buf.replaceFunctions(oldData.getterFunctions(), newData.getterFunctions(), oldBaseAddress); Name newBaseAddress = oldBaseAddress.withConstraint(newData); buf.renameParameter(0, newBaseAddress); Name getCombiner = new Name(newData.getterFunction(oldData.fieldCount()), newBaseAddress); Object[] combinerArgs = new Object[1 + combinerArity]; combinerArgs[0] = getCombiner; Name newParam = null; if (keepArguments) { for (int i = 0; i < combinerArity; i++) { combinerArgs[i + 1] = lambdaForm.parameter(1 + argPositions[i]); assert (basicType(combinerType.parameterType(i)) == lambdaForm.parameterType(1 + argPositions[i])); } } else { newParam = new Name(pos, BasicType.basicType(combinerType.returnType())); for (int i = 0; i < combinerArity; i++) { int argPos = 1 + argPositions[i]; if (argPos == pos) { combinerArgs[i + 1] = newParam; } else { combinerArgs[i + 1] = lambdaForm.parameter(argPos); } assert (basicType(combinerType.parameterType(i)) == lambdaForm.parameterType(1 + argPositions[i])); } } Name callCombiner = new Name(combinerType, combinerArgs); // insert the two new expressions int exprPos = lambdaForm.arity(); buf.insertExpression(exprPos+0, getCombiner); buf.insertExpression(exprPos+1, callCombiner); // insert new arguments, if needed int argPos = pos + resultArity; // skip result parameter if (newParam != null) { buf.insertParameter(argPos++, newParam); exprPos++; } assert(buf.lastIndexOf(callCombiner) == exprPos+1); if (!dropResult) { buf.replaceParameterByCopy(pos, exprPos+1); } return buf.endEdit(); } LambdaForm filterReturnForm(BasicType newType, boolean constantZero) { TransformKey key = TransformKey.of(FILTER_RETURN, constantZero ? (byte) 1 : (byte)0, newType.ordinal()); LambdaForm form = getInCache(key); if (form != null) { assert(form.arity == lambdaForm.arity); assert(form.returnType() == newType); return form; } LambdaFormBuffer buf = buffer(); buf.startEdit(); int insPos = lambdaForm.names.length; Name callFilter; if (constantZero) { // Synthesize a constant zero value for the given type. if (newType == V_TYPE) callFilter = null; else callFilter = new Name(constantZero(newType)); } else { BoundMethodHandle.SpeciesData oldData = oldSpeciesData(); BoundMethodHandle.SpeciesData newData = newSpeciesData(L_TYPE); // The newly created LF will run with a different BMH. // Switch over any pre-existing BMH field references to the new BMH class. Name oldBaseAddress = lambdaForm.parameter(0); // BMH holding the values buf.replaceFunctions(oldData.getterFunctions(), newData.getterFunctions(), oldBaseAddress); Name newBaseAddress = oldBaseAddress.withConstraint(newData); buf.renameParameter(0, newBaseAddress); Name getFilter = new Name(newData.getterFunction(oldData.fieldCount()), newBaseAddress); buf.insertExpression(insPos++, getFilter); BasicType oldType = lambdaForm.returnType(); if (oldType == V_TYPE) { MethodType filterType = MethodType.methodType(newType.basicTypeClass()); callFilter = new Name(filterType, getFilter); } else { MethodType filterType = MethodType.methodType(newType.basicTypeClass(), oldType.basicTypeClass()); callFilter = new Name(filterType, getFilter, lambdaForm.names[lambdaForm.result]); } } if (callFilter != null) buf.insertExpression(insPos++, callFilter); buf.setResult(callFilter); form = buf.endEdit(); return putInCache(key, form); } LambdaForm collectReturnValueForm(MethodType combinerType) { LambdaFormBuffer buf = buffer(); buf.startEdit(); int combinerArity = combinerType.parameterCount(); int argPos = lambdaForm.arity(); int exprPos = lambdaForm.names.length; BoundMethodHandle.SpeciesData oldData = oldSpeciesData(); BoundMethodHandle.SpeciesData newData = newSpeciesData(L_TYPE); // The newly created LF will run with a different BMH. // Switch over any pre-existing BMH field references to the new BMH class. Name oldBaseAddress = lambdaForm.parameter(0); // BMH holding the values buf.replaceFunctions(oldData.getterFunctions(), newData.getterFunctions(), oldBaseAddress); Name newBaseAddress = oldBaseAddress.withConstraint(newData); buf.renameParameter(0, newBaseAddress); // Now we set up the call to the filter Name getCombiner = new Name(newData.getterFunction(oldData.fieldCount()), newBaseAddress); Object[] combinerArgs = new Object[combinerArity + 1]; combinerArgs[0] = getCombiner; // first (synthetic) argument should be the MH that acts as a target of the invoke // set up additional adapter parameters (in case the combiner is not a unary function) Name[] newParams = new Name[combinerArity - 1]; // last combiner parameter is the return adapter for (int i = 0; i < newParams.length; i++) { newParams[i] = new Name(argPos + i, basicType(combinerType.parameterType(i))); } // set up remaining filter parameters to point to the corresponding adapter parameters (see above) System.arraycopy(newParams, 0, combinerArgs, 1, combinerArity - 1); // the last filter argument is set to point at the result of the target method handle combinerArgs[combinerArity] = buf.name(lambdaForm.names.length - 1); Name callCombiner = new Name(combinerType, combinerArgs); // insert the two new expressions buf.insertExpression(exprPos, getCombiner); buf.insertExpression(exprPos + 1, callCombiner); // insert additional arguments int insPos = argPos; for (Name newParam : newParams) { buf.insertParameter(insPos++, newParam); } buf.setResult(callCombiner); return buf.endEdit(); } LambdaForm foldArgumentsForm(int foldPos, boolean dropResult, MethodType combinerType) { int combinerArity = combinerType.parameterCount(); byte kind = (dropResult ? FOLD_ARGS_TO_VOID : FOLD_ARGS); TransformKey key = TransformKey.of(kind, foldPos, combinerArity); LambdaForm form = getInCache(key); if (form != null) { assert(form.arity == lambdaForm.arity - (kind == FOLD_ARGS ? 1 : 0)); return form; } form = makeArgumentCombinationForm(foldPos, combinerType, true, dropResult); return putInCache(key, form); } LambdaForm foldArgumentsForm(int foldPos, boolean dropResult, MethodType combinerType, int ... argPositions) { TransformKey key = TransformKey.of(FOLD_SELECT_ARGS, foldPos, dropResult ? 1 : 0, argPositions); LambdaForm form = getInCache(key); if (form != null) { assert(form.arity == lambdaForm.arity - (dropResult ? 0 : 1)); return form; } form = makeArgumentCombinationForm(foldPos, combinerType, argPositions, true, dropResult); return putInCache(key, form); } LambdaForm filterArgumentsForm(int filterPos, MethodType combinerType, int ... argPositions) { TransformKey key = TransformKey.of(FILTER_SELECT_ARGS, filterPos, argPositions); LambdaForm form = getInCache(key); if (form != null) { assert(form.arity == lambdaForm.arity); return form; } form = makeArgumentCombinationForm(filterPos, combinerType, argPositions, false, false); return putInCache(key, form); } LambdaForm permuteArgumentsForm(int skip, int[] reorder) { assert(skip == 1); // skip only the leading MH argument, names[0] int length = lambdaForm.names.length; int outArgs = reorder.length; int inTypes = 0; boolean nullPerm = true; for (int i = 0; i < reorder.length; i++) { int inArg = reorder[i]; if (inArg != i) nullPerm = false; inTypes = Math.max(inTypes, inArg+1); } assert(skip + reorder.length == lambdaForm.arity); if (nullPerm) return lambdaForm; // do not bother to cache TransformKey key = TransformKey.of(PERMUTE_ARGS, reorder); LambdaForm form = getInCache(key); if (form != null) { assert(form.arity == skip+inTypes) : form; return form; } BasicType[] types = new BasicType[inTypes]; for (int i = 0; i < outArgs; i++) { int inArg = reorder[i]; types[inArg] = lambdaForm.names[skip + i].type; } assert (skip + outArgs == lambdaForm.arity); assert (permutedTypesMatch(reorder, types, lambdaForm.names, skip)); int pos = 0; while (pos < outArgs && reorder[pos] == pos) { pos += 1; } Name[] names2 = new Name[length - outArgs + inTypes]; System.arraycopy(lambdaForm.names, 0, names2, 0, skip + pos); int bodyLength = length - lambdaForm.arity; System.arraycopy(lambdaForm.names, skip + outArgs, names2, skip + inTypes, bodyLength); int arity2 = names2.length - bodyLength; int result2 = lambdaForm.result; if (result2 >= skip) { if (result2 < skip + outArgs) { result2 = reorder[result2 - skip] + skip; } else { result2 = result2 - outArgs + inTypes; } } for (int j = pos; j < outArgs; j++) { Name n = lambdaForm.names[skip + j]; int i = reorder[j]; Name n2 = names2[skip + i]; if (n2 == null) { names2[skip + i] = n2 = new Name(types[i]); } else { assert (n2.type == types[i]); } for (int k = arity2; k < names2.length; k++) { names2[k] = names2[k].replaceName(n, n2); } } for (int i = skip + pos; i < arity2; i++) { if (names2[i] == null) { names2[i] = argument(i, types[i - skip]); } } for (int j = lambdaForm.arity; j < lambdaForm.names.length; j++) { int i = j - lambdaForm.arity + arity2; Name n = lambdaForm.names[j]; Name n2 = names2[i]; if (n != n2) { for (int k = i + 1; k < names2.length; k++) { names2[k] = names2[k].replaceName(n, n2); } } } form = LambdaForm.create(arity2, names2, result2); return putInCache(key, form); } LambdaForm noteLoopLocalTypesForm(int pos, BasicType[] localTypes) { assert(lambdaForm.isLoop(pos)); int[] desc = BasicType.basicTypeOrds(localTypes); desc = Arrays.copyOf(desc, desc.length + 1); desc[desc.length - 1] = pos; TransformKey key = TransformKey.of(LOCAL_TYPES, desc); LambdaForm form = getInCache(key); if (form != null) { return form; } // replace the null entry in the MHImpl.loop invocation with localTypes Name invokeLoop = lambdaForm.names[pos + 1]; assert(invokeLoop.function.equals(MethodHandleImpl.getFunction(NF_loop))); Object[] args = Arrays.copyOf(invokeLoop.arguments, invokeLoop.arguments.length); assert(args[0] == null); args[0] = localTypes; LambdaFormBuffer buf = buffer(); buf.startEdit(); buf.changeName(pos + 1, new Name(MethodHandleImpl.getFunction(NF_loop), args)); form = buf.endEdit(); return putInCache(key, form); } static boolean permutedTypesMatch(int[] reorder, BasicType[] types, Name[] names, int skip) { for (int i = 0; i < reorder.length; i++) { assert (names[skip + i].isParam()); assert (names[skip + i].type == types[reorder[i]]); } return true; } }