|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package java.lang.invoke; |
|
|
|
import sun.invoke.util.Wrapper; |
|
|
|
import java.lang.ref.SoftReference; |
|
import java.util.Arrays; |
|
import java.util.Collections; |
|
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; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
class LambdaFormEditor { |
|
final LambdaForm lambdaForm; |
|
|
|
private LambdaFormEditor(LambdaForm lambdaForm) { |
|
this.lambdaForm = lambdaForm; |
|
} |
|
|
|
|
|
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. |
|
|
|
return new LambdaFormEditor(lambdaForm.uncustomize()); |
|
} |
|
|
|
// Transform types |
|
|
|
private static final byte |
|
BIND_ARG = 1, |
|
ADD_ARG = 2, |
|
DUP_ARG = 3, |
|
SPREAD_ARGS = 4, |
|
FILTER_ARG = 5, |
|
FILTER_RETURN = 6, |
|
FILTER_RETURN_TO_ZERO = 7, |
|
COLLECT_ARGS = 8, |
|
COLLECT_ARGS_TO_VOID = 9, |
|
COLLECT_ARGS_TO_ARRAY = 10, |
|
FOLD_ARGS = 11, |
|
FOLD_ARGS_TO_VOID = 12, |
|
PERMUTE_ARGS = 13, |
|
LOCAL_TYPES = 14, |
|
FOLD_SELECT_ARGS = 15, |
|
FOLD_SELECT_ARGS_TO_VOID = 16, |
|
FILTER_SELECT_ARGS = 17, |
|
REPEAT_FILTER_ARGS = 18; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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(); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
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) { |
|
this.fullBytes = fullBytes; |
|
this.packedBytes = 0; |
|
} |
|
|
|
private TransformKey(long packedBytes, byte[] fullBytes) { |
|
this.fullBytes = fullBytes; |
|
this.packedBytes = packedBytes; |
|
} |
|
|
|
private static byte bval(int b) { |
|
assert((b & 0xFF) == b); |
|
return (byte)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)); |
|
} |
|
private static final byte[] NO_BYTES = {}; |
|
static TransformKey of(byte kind, int... b123) { |
|
return ofBothArrays(kind, b123, NO_BYTES); |
|
} |
|
|
|
static TransformKey of(byte kind, int b1, int[] b23456) { |
|
byte[] fullBytes = new byte[b23456.length + 2]; |
|
fullBytes[0] = kind; |
|
fullBytes[1] = bval(b1); |
|
for (int i = 0; i < b23456.length; i++) { |
|
fullBytes[i + 2] = TransformKey.bval(b23456[i]); |
|
} |
|
long packedBytes = packedBytes(fullBytes); |
|
if (packedBytes != 0) |
|
return new TransformKey(packedBytes); |
|
else |
|
return new TransformKey(fullBytes); |
|
} |
|
|
|
static TransformKey of(byte kind, int b1, int b2, byte[] b345) { |
|
return ofBothArrays(kind, new int[]{ b1, b2 }, b345); |
|
} |
|
private static TransformKey ofBothArrays(byte kind, int[] b123, byte[] b456) { |
|
byte[] fullBytes = new byte[1 + b123.length + b456.length]; |
|
int i = 0; |
|
fullBytes[i++] = bval(kind); |
|
for (int bv : b123) { |
|
fullBytes[i++] = bval(bv); |
|
} |
|
for (byte bv : b456) { |
|
fullBytes[i++] = bv; |
|
} |
|
long packedBytes = packedBytes(fullBytes); |
|
if (packedBytes != 0) |
|
return new TransformKey(packedBytes); |
|
else |
|
return new TransformKey(fullBytes); |
|
} |
|
|
|
private static final boolean STRESS_TEST = false; |
|
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[] 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 << 0*PACKED_BYTE_SIZE) |
|
| (b1 << 1*PACKED_BYTE_SIZE)); |
|
} |
|
private static long packedBytes(int b0, int b1, int b2) { |
|
assert(inRange(b0 | b1 | b2)); |
|
return ( (b0 << 0*PACKED_BYTE_SIZE) |
|
| (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 << 0*PACKED_BYTE_SIZE) |
|
| (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); |
|
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) { |
|
assert(fullBytes == null); |
|
return Long.hashCode(packedBytes); |
|
} |
|
return Arrays.hashCode(fullBytes); |
|
} |
|
} |
|
|
|
|
|
private LambdaForm getInCache(TransformKey key) { |
|
|
|
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) { |
|
|
|
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; |
|
} |
|
|
|
|
|
private static final int MIN_CACHE_ARRAY_SIZE = 4, MAX_CACHE_ARRAY_SIZE = 16; |
|
|
|
|
|
|
|
*/ |
|
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) { |
|
lambdaForm.transformCache = transform; |
|
return form; |
|
} |
|
|
|
ta = new Transform[MIN_CACHE_ARRAY_SIZE]; |
|
ta[0] = k; |
|
lambdaForm.transformCache = ta; |
|
} else { |
|
|
|
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; |
|
} |
|
} |
|
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; |
|
|
|
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((byte) type.ordinal()); |
|
} |
|
|
|
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); |
|
Name newBaseAddress; |
|
NamedFunction getter = newData.getterFunction(oldData.fieldCount()); |
|
|
|
if (pos != 0) { |
|
// The newly created LF will run with a different BMH. |
|
|
|
buf.replaceFunctions(oldData.getterFunctions(), newData.getterFunctions(), oldBaseAddress); |
|
newBaseAddress = oldBaseAddress.withConstraint(newData); |
|
buf.renameParameter(0, newBaseAddress); |
|
buf.replaceParameterByNewExpression(pos, new Name(getter, newBaseAddress)); |
|
} else { |
|
|
|
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); |
|
|
|
Name spreadParam = new Name(L_TYPE); |
|
Name checkSpread = new Name(MethodHandleImpl.getFunction(MethodHandleImpl.NF_checkSpreadArgument), |
|
spreadParam, arrayLength); |
|
|
|
|
|
int exprPos = lambdaForm.arity(); |
|
buf.insertExpression(exprPos++, checkSpread); |
|
|
|
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))); |
|
} |
|
byte[] newTypes = BasicType.basicTypesOrd(collectorType.ptypes()); |
|
byte kind = (dropResult ? COLLECT_ARGS_TO_VOID : COLLECT_ARGS); |
|
if (dropResult && collectorArity == 0) pos = 1; |
|
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); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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); |
|
buf.replaceFunctions(oldData.getterFunctions(), newData.getterFunctions(), oldBaseAddress); |
|
Name newBaseAddress = oldBaseAddress.withConstraint(newData); |
|
buf.renameParameter(0, newBaseAddress); |
|
|
|
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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); |
|
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); |
|
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); |
|
|
|
|
|
int exprPos = lambdaForm.arity(); |
|
buf.insertExpression(exprPos+0, getCombiner); |
|
buf.insertExpression(exprPos+1, callCombiner); |
|
|
|
// insert new arguments, if needed |
|
int argPos = pos + resultArity; |
|
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); |
|
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); |
|
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); |
|
|
|
|
|
int exprPos = lambdaForm.arity(); |
|
buf.insertExpression(exprPos+0, getCombiner); |
|
buf.insertExpression(exprPos+1, callCombiner); |
|
|
|
// insert new arguments, if needed |
|
int argPos = pos + resultArity; |
|
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) { |
|
byte kind = (constantZero ? FILTER_RETURN_TO_ZERO : FILTER_RETURN); |
|
TransformKey key = TransformKey.of(kind, 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) { |
|
|
|
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); |
|
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); |
|
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[combinerArity + 1]; |
|
combinerArgs[0] = getCombiner; |
|
|
|
// set up additional adapter parameters (in case the combiner is not a unary function) |
|
Name[] newParams = new Name[combinerArity - 1]; |
|
for (int i = 0; i < newParams.length; i++) { |
|
newParams[i] = new Name(argPos + i, basicType(combinerType.parameterType(i))); |
|
} |
|
|
|
|
|
System.arraycopy(newParams, 0, |
|
combinerArgs, 1, combinerArity - 1); |
|
|
|
|
|
combinerArgs[combinerArity] = buf.name(lambdaForm.names.length - 1); |
|
Name callCombiner = new Name(combinerType, combinerArgs); |
|
|
|
|
|
buf.insertExpression(exprPos, getCombiner); |
|
buf.insertExpression(exprPos + 1, callCombiner); |
|
|
|
|
|
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) { |
|
byte kind = (dropResult ? FOLD_SELECT_ARGS_TO_VOID : FOLD_SELECT_ARGS); |
|
TransformKey key = TransformKey.of(kind, foldPos, argPositions); |
|
LambdaForm form = getInCache(key); |
|
if (form != null) { |
|
assert(form.arity == lambdaForm.arity - (kind == FOLD_SELECT_ARGS ? 1 : 0)); |
|
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); |
|
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; |
|
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 = new LambdaForm(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; |
|
} |
|
|
|
|
|
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; |
|
} |
|
} |