8057922: Improve LambdaForm sharing by using LambdaFormEditor more extensively
Reviewed-by: vlivanov, psandoz
This commit is contained in:
parent
30f687e214
commit
dbbce7862e
@ -802,28 +802,6 @@ class LambdaForm {
|
||||
return rval;
|
||||
}
|
||||
|
||||
//** This transform is applied (statically) to every name.function. */
|
||||
/*
|
||||
private static MethodHandle eraseSubwordTypes(MethodHandle mh) {
|
||||
MethodType mt = mh.type();
|
||||
if (mt.hasPrimitives()) {
|
||||
mt = mt.changeReturnType(eraseSubwordType(mt.returnType()));
|
||||
for (int i = 0; i < mt.parameterCount(); i++) {
|
||||
mt = mt.changeParameterType(i, eraseSubwordType(mt.parameterType(i)));
|
||||
}
|
||||
mh = MethodHandles.explicitCastArguments(mh, mt);
|
||||
}
|
||||
return mh;
|
||||
}
|
||||
private static Class<?> eraseSubwordType(Class<?> type) {
|
||||
if (!type.isPrimitive()) return type;
|
||||
if (type == int.class) return type;
|
||||
Wrapper w = Wrapper.forPrimitiveType(type);
|
||||
if (w.isSubwordOrInt()) return int.class;
|
||||
return type;
|
||||
}
|
||||
*/
|
||||
|
||||
static void traceInterpreter(String event, Object obj, Object... args) {
|
||||
if (TRACE_INTERPRETER) {
|
||||
System.out.println("LFI: "+event+" "+(obj != null ? obj : "")+(args != null && args.length != 0 ? Arrays.asList(args) : ""));
|
||||
|
@ -59,7 +59,7 @@ final class LambdaFormBuffer {
|
||||
resultName = names[result];
|
||||
}
|
||||
|
||||
LambdaForm lambdaForm() {
|
||||
private LambdaForm lambdaForm() {
|
||||
assert(!inTrans()); // need endEdit call to tidy things up
|
||||
return new LambdaForm(debugName, arity, nameArray(), resultIndex());
|
||||
}
|
||||
@ -276,7 +276,7 @@ final class LambdaFormBuffer {
|
||||
}
|
||||
|
||||
/** Finish a transaction. */
|
||||
void endEdit() {
|
||||
LambdaForm endEdit() {
|
||||
assert(verifyFirstChange());
|
||||
// Assuming names have been changed pairwise from originalNames[i] to names[i],
|
||||
// update arguments to ensure referential integrity.
|
||||
@ -316,6 +316,7 @@ final class LambdaFormBuffer {
|
||||
arity -= exprp;
|
||||
}
|
||||
assert(verifyArity());
|
||||
return lambdaForm();
|
||||
}
|
||||
|
||||
private Name[] copyNamesInto(Name[] buffer) {
|
||||
|
@ -443,8 +443,383 @@ class LambdaFormEditor {
|
||||
buf.insertParameter(0, newBaseAddress);
|
||||
}
|
||||
|
||||
buf.endEdit();
|
||||
form = buf.lambdaForm();
|
||||
form = buf.endEdit();
|
||||
return putInCache(key, form);
|
||||
}
|
||||
|
||||
LambdaForm addArgumentForm(int pos, BasicType type) {
|
||||
Transform key = Transform.of(Transform.Kind.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) {
|
||||
Transform key = Transform.of(Transform.Kind.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();
|
||||
}
|
||||
}
|
||||
Transform key = Transform.of(Transform.Kind.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.Lazy.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(aload, 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)));
|
||||
}
|
||||
BasicType[] newTypes = BasicType.basicTypes(collectorType.parameterList());
|
||||
Transform.Kind kind = (dropResult
|
||||
? Transform.Kind.COLLECT_ARGS_TO_VOID
|
||||
: Transform.Kind.COLLECT_ARGS);
|
||||
if (dropResult && collectorArity == 0) pos = 1; // pure side effect
|
||||
Transform key = Transform.of(kind, pos, collectorArity, BasicType.basicTypesOrd(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 collectArgumentArrayForm(int pos, MethodHandle arrayCollector) {
|
||||
MethodType collectorType = arrayCollector.type();
|
||||
int collectorArity = collectorType.parameterCount();
|
||||
assert(arrayCollector.intrinsicName() == Intrinsic.NEW_ARRAY);
|
||||
Class<?> arrayType = collectorType.returnType();
|
||||
Class<?> elementType = arrayType.getComponentType();
|
||||
BasicType argType = basicType(elementType);
|
||||
int argTypeKey = argType.ordinal();
|
||||
if (argType.basicTypeClass() != elementType) {
|
||||
// return null if it requires more metadata (like String[].class)
|
||||
if (!elementType.isPrimitive())
|
||||
return null;
|
||||
argTypeKey = TYPE_LIMIT + Wrapper.forPrimitiveType(elementType).ordinal();
|
||||
}
|
||||
assert(collectorType.parameterList().equals(Collections.nCopies(collectorArity, elementType)));
|
||||
Transform.Kind kind = Transform.Kind.COLLECT_ARGS_TO_ARRAY;
|
||||
Transform key = Transform.of(kind, pos, collectorArity, argTypeKey);
|
||||
LambdaForm form = getInCache(key);
|
||||
if (form != null) {
|
||||
assert(form.arity == lambdaForm.arity - 1 + collectorArity);
|
||||
return form;
|
||||
}
|
||||
LambdaFormBuffer buf = buffer();
|
||||
buf.startEdit();
|
||||
|
||||
assert(pos + 1 <= lambdaForm.arity);
|
||||
assert(pos > 0); // cannot filter the MH arg itself
|
||||
|
||||
Name[] newParams = new Name[collectorArity];
|
||||
for (int i = 0; i < collectorArity; i++) {
|
||||
newParams[i] = new Name(pos + i, argType);
|
||||
}
|
||||
Name callCombiner = new Name(arrayCollector, (Object[]) /*...*/ newParams);
|
||||
|
||||
// insert the new expression
|
||||
int exprPos = lambdaForm.arity();
|
||||
buf.insertExpression(exprPos, callCombiner);
|
||||
|
||||
// insert new arguments
|
||||
int argPos = pos + 1; // skip result parameter
|
||||
for (Name newParam : newParams) {
|
||||
buf.insertParameter(argPos++, newParam);
|
||||
}
|
||||
assert(buf.lastIndexOf(callCombiner) == exprPos+newParams.length);
|
||||
buf.replaceParameterByCopy(pos, exprPos+newParams.length);
|
||||
|
||||
form = buf.endEdit();
|
||||
return putInCache(key, form);
|
||||
}
|
||||
|
||||
LambdaForm filterArgumentForm(int pos, BasicType newType) {
|
||||
Transform key = Transform.of(Transform.Kind.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);
|
||||
}
|
||||
|
||||
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];
|
||||
BasicType[] newTypes = basicTypes(combinerType.parameterList());
|
||||
for (int i = 0; i < newTypes.length; i++) {
|
||||
newParams[i] = new Name(pos + i, newTypes[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();
|
||||
}
|
||||
|
||||
LambdaForm filterReturnForm(BasicType newType, boolean constantZero) {
|
||||
Transform.Kind kind = (constantZero ? Transform.Kind.FILTER_RETURN_TO_ZERO : Transform.Kind.FILTER_RETURN);
|
||||
Transform key = Transform.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) {
|
||||
// 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 foldArgumentsForm(int foldPos, boolean dropResult, MethodType combinerType) {
|
||||
int combinerArity = combinerType.parameterCount();
|
||||
Transform.Kind kind = (dropResult ? Transform.Kind.FOLD_ARGS_TO_VOID : Transform.Kind.FOLD_ARGS);
|
||||
Transform key = Transform.of(kind, foldPos, combinerArity);
|
||||
LambdaForm form = getInCache(key);
|
||||
if (form != null) {
|
||||
assert(form.arity == lambdaForm.arity - (kind == Transform.Kind.FOLD_ARGS ? 1 : 0));
|
||||
return form;
|
||||
}
|
||||
form = makeArgumentCombinationForm(foldPos, combinerType, true, dropResult);
|
||||
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
|
||||
Transform key = Transform.of(Transform.Kind.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 >= 0) {
|
||||
if (result2 < skip + outArgs) {
|
||||
result2 = reorder[result2 - 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(lambdaForm.debugName, arity2, names2, result2);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -864,9 +864,18 @@ assertEquals("[A, B, C]", (String) caToString2.invokeExact('A', "BC".toCharArray
|
||||
* @see #asCollector
|
||||
*/
|
||||
public MethodHandle asSpreader(Class<?> arrayType, int arrayLength) {
|
||||
asSpreaderChecks(arrayType, arrayLength);
|
||||
int spreadArgPos = type.parameterCount() - arrayLength;
|
||||
return MethodHandleImpl.makeSpreadArguments(this, arrayType, spreadArgPos, arrayLength);
|
||||
MethodType postSpreadType = asSpreaderChecks(arrayType, arrayLength);
|
||||
int arity = type().parameterCount();
|
||||
int spreadArgPos = arity - arrayLength;
|
||||
if (USE_LAMBDA_FORM_EDITOR) {
|
||||
MethodHandle afterSpread = this.asType(postSpreadType);
|
||||
BoundMethodHandle mh = afterSpread.rebind();
|
||||
LambdaForm lform = mh.editor().spreadArgumentsForm(1 + spreadArgPos, arrayType, arrayLength);
|
||||
MethodType preSpreadType = postSpreadType.replaceParameterTypes(spreadArgPos, arity, arrayType);
|
||||
return mh.copyWith(preSpreadType, lform);
|
||||
} else {
|
||||
return MethodHandleImpl.makeSpreadArguments(this, arrayType, spreadArgPos, arrayLength);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -986,12 +995,24 @@ assertEquals("[123]", (String) longsToString.invokeExact((long)123));
|
||||
*/
|
||||
public MethodHandle asCollector(Class<?> arrayType, int arrayLength) {
|
||||
asCollectorChecks(arrayType, arrayLength);
|
||||
int collectArgPos = type().parameterCount()-1;
|
||||
MethodHandle target = this;
|
||||
if (arrayType != type().parameterType(collectArgPos))
|
||||
target = MethodHandleImpl.makePairwiseConvert(this, type().changeParameterType(collectArgPos, arrayType), true);
|
||||
MethodHandle collector = MethodHandleImpl.varargsArray(arrayType, arrayLength);
|
||||
return MethodHandles.collectArguments(target, collectArgPos, collector);
|
||||
int collectArgPos = type().parameterCount() - 1;
|
||||
if (USE_LAMBDA_FORM_EDITOR) {
|
||||
BoundMethodHandle mh = rebind();
|
||||
MethodType resultType = type().asCollectorType(arrayType, arrayLength);
|
||||
MethodHandle newArray = MethodHandleImpl.varargsArray(arrayType, arrayLength);
|
||||
LambdaForm lform = mh.editor().collectArgumentArrayForm(1 + collectArgPos, newArray);
|
||||
if (lform != null) {
|
||||
return mh.copyWith(resultType, lform);
|
||||
}
|
||||
lform = mh.editor().collectArgumentsForm(1 + collectArgPos, newArray.type().basicType());
|
||||
return mh.copyWithExtendL(resultType, lform, newArray);
|
||||
} else {
|
||||
MethodHandle target = this;
|
||||
if (arrayType != type().parameterType(collectArgPos))
|
||||
target = MethodHandleImpl.makePairwiseConvert(this, type().changeParameterType(collectArgPos, arrayType), true);
|
||||
MethodHandle collector = MethodHandleImpl.varargsArray(arrayType, arrayLength);
|
||||
return MethodHandles.collectArguments(target, collectArgPos, collector);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -191,7 +191,11 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
assert(dstType.parameterCount() == target.type().parameterCount());
|
||||
if (srcType == dstType)
|
||||
return target;
|
||||
return makePairwiseConvertIndirect(target, srcType, strict, monobox);
|
||||
if (USE_LAMBDA_FORM_EDITOR) {
|
||||
return makePairwiseConvertByEditor(target, srcType, strict, monobox);
|
||||
} else {
|
||||
return makePairwiseConvertIndirect(target, srcType, strict, monobox);
|
||||
}
|
||||
}
|
||||
|
||||
private static int countNonNull(Object[] array) {
|
||||
@ -202,6 +206,63 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
return count;
|
||||
}
|
||||
|
||||
static MethodHandle makePairwiseConvertByEditor(MethodHandle target, MethodType srcType,
|
||||
boolean strict, boolean monobox) {
|
||||
Object[] convSpecs = computeValueConversions(srcType, target.type(), strict, monobox);
|
||||
int convCount = countNonNull(convSpecs);
|
||||
if (convCount == 0)
|
||||
return target.viewAsType(srcType, strict);
|
||||
MethodType basicSrcType = srcType.basicType();
|
||||
MethodType midType = target.type().basicType();
|
||||
BoundMethodHandle mh = target.rebind();
|
||||
// FIXME: Reduce number of bindings when there is more than one Class conversion.
|
||||
// FIXME: Reduce number of bindings when there are repeated conversions.
|
||||
for (int i = 0; i < convSpecs.length-1; i++) {
|
||||
Object convSpec = convSpecs[i];
|
||||
if (convSpec == null) continue;
|
||||
MethodHandle fn;
|
||||
if (convSpec instanceof Class) {
|
||||
fn = Lazy.MH_castReference.bindTo(convSpec);
|
||||
} else {
|
||||
fn = (MethodHandle) convSpec;
|
||||
}
|
||||
Class<?> newType = basicSrcType.parameterType(i);
|
||||
if (--convCount == 0)
|
||||
midType = srcType;
|
||||
else
|
||||
midType = midType.changeParameterType(i, newType);
|
||||
LambdaForm form2 = mh.editor().filterArgumentForm(1+i, BasicType.basicType(newType));
|
||||
mh = mh.copyWithExtendL(midType, form2, fn);
|
||||
mh = mh.rebind();
|
||||
}
|
||||
Object convSpec = convSpecs[convSpecs.length-1];
|
||||
if (convSpec != null) {
|
||||
MethodHandle fn;
|
||||
if (convSpec instanceof Class) {
|
||||
if (convSpec == void.class)
|
||||
fn = null;
|
||||
else
|
||||
fn = Lazy.MH_castReference.bindTo(convSpec);
|
||||
} else {
|
||||
fn = (MethodHandle) convSpec;
|
||||
}
|
||||
Class<?> newType = basicSrcType.returnType();
|
||||
assert(--convCount == 0);
|
||||
midType = srcType;
|
||||
if (fn != null) {
|
||||
mh = mh.rebind(); // rebind if too complex
|
||||
LambdaForm form2 = mh.editor().filterReturnForm(BasicType.basicType(newType), false);
|
||||
mh = mh.copyWithExtendL(midType, form2, fn);
|
||||
} else {
|
||||
LambdaForm form2 = mh.editor().filterReturnForm(BasicType.basicType(newType), true);
|
||||
mh = mh.copyWith(midType, form2);
|
||||
}
|
||||
}
|
||||
assert(convCount == 0);
|
||||
assert(mh.type().equals(srcType));
|
||||
return mh;
|
||||
}
|
||||
|
||||
static MethodHandle makePairwiseConvertIndirect(MethodHandle target, MethodType srcType,
|
||||
boolean strict, boolean monobox) {
|
||||
// Calculate extra arguments (temporaries) required in the names array.
|
||||
|
@ -45,19 +45,21 @@ import sun.misc.Unsafe;
|
||||
static final boolean DUMP_CLASS_FILES;
|
||||
static final boolean TRACE_INTERPRETER;
|
||||
static final boolean TRACE_METHOD_LINKAGE;
|
||||
static final boolean USE_LAMBDA_FORM_EDITOR;
|
||||
static final int COMPILE_THRESHOLD;
|
||||
static final int PROFILE_LEVEL;
|
||||
|
||||
static {
|
||||
final Object[] values = { false, false, false, false, null, null };
|
||||
final Object[] values = { false, false, false, false, false, null, null };
|
||||
AccessController.doPrivileged(new PrivilegedAction<Void>() {
|
||||
public Void run() {
|
||||
values[0] = Boolean.getBoolean("java.lang.invoke.MethodHandle.DEBUG_NAMES");
|
||||
values[1] = Boolean.getBoolean("java.lang.invoke.MethodHandle.DUMP_CLASS_FILES");
|
||||
values[2] = Boolean.getBoolean("java.lang.invoke.MethodHandle.TRACE_INTERPRETER");
|
||||
values[3] = Boolean.getBoolean("java.lang.invoke.MethodHandle.TRACE_METHOD_LINKAGE");
|
||||
values[4] = Integer.getInteger("java.lang.invoke.MethodHandle.COMPILE_THRESHOLD", 30);
|
||||
values[5] = Integer.getInteger("java.lang.invoke.MethodHandle.PROFILE_LEVEL", 0);
|
||||
values[4] = Boolean.getBoolean("java.lang.invoke.MethodHandle.USE_LF_EDITOR");
|
||||
values[5] = Integer.getInteger("java.lang.invoke.MethodHandle.COMPILE_THRESHOLD", 30);
|
||||
values[6] = Integer.getInteger("java.lang.invoke.MethodHandle.PROFILE_LEVEL", 0);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
@ -65,8 +67,9 @@ import sun.misc.Unsafe;
|
||||
DUMP_CLASS_FILES = (Boolean) values[1];
|
||||
TRACE_INTERPRETER = (Boolean) values[2];
|
||||
TRACE_METHOD_LINKAGE = (Boolean) values[3];
|
||||
COMPILE_THRESHOLD = (Integer) values[4];
|
||||
PROFILE_LEVEL = (Integer) values[5];
|
||||
USE_LAMBDA_FORM_EDITOR = (Boolean) values[4];
|
||||
COMPILE_THRESHOLD = (Integer) values[5];
|
||||
PROFILE_LEVEL = (Integer) values[6];
|
||||
}
|
||||
|
||||
/** Tell if any of the debugging switches are turned on.
|
||||
|
@ -28,7 +28,6 @@ package java.lang.invoke;
|
||||
import java.lang.reflect.*;
|
||||
import java.util.BitSet;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
import sun.invoke.util.ValueConversions;
|
||||
@ -2094,23 +2093,86 @@ assert((int)twice.invokeExact(21) == 42);
|
||||
*/
|
||||
public static
|
||||
MethodHandle permuteArguments(MethodHandle target, MethodType newType, int... reorder) {
|
||||
reorder = reorder.clone();
|
||||
permuteArgumentChecks(reorder, newType, target.type());
|
||||
// first detect dropped arguments and handle them separately
|
||||
MethodHandle originalTarget = target;
|
||||
int newArity = newType.parameterCount();
|
||||
for (int dropIdx; (dropIdx = findFirstDrop(reorder, newArity)) >= 0; ) {
|
||||
// dropIdx is missing from reorder; add it in at the end
|
||||
int oldArity = reorder.length;
|
||||
target = dropArguments(target, oldArity, newType.parameterType(dropIdx));
|
||||
reorder = Arrays.copyOf(reorder, oldArity+1);
|
||||
reorder[oldArity] = dropIdx;
|
||||
reorder = reorder.clone(); // get a private copy
|
||||
MethodType oldType = target.type();
|
||||
permuteArgumentChecks(reorder, newType, oldType);
|
||||
if (USE_LAMBDA_FORM_EDITOR) {
|
||||
// first detect dropped arguments and handle them separately
|
||||
int[] originalReorder = reorder;
|
||||
BoundMethodHandle result = target.rebind();
|
||||
LambdaForm form = result.form;
|
||||
int newArity = newType.parameterCount();
|
||||
// Normalize the reordering into a real permutation,
|
||||
// by removing duplicates and adding dropped elements.
|
||||
// This somewhat improves lambda form caching, as well
|
||||
// as simplifying the transform by breaking it up into steps.
|
||||
for (int ddIdx; (ddIdx = findFirstDupOrDrop(reorder, newArity)) != 0; ) {
|
||||
if (ddIdx > 0) {
|
||||
// We found a duplicated entry at reorder[ddIdx].
|
||||
// Example: (x,y,z)->asList(x,y,z)
|
||||
// permuted by [1*,0,1] => (a0,a1)=>asList(a1,a0,a1)
|
||||
// permuted by [0,1,0*] => (a0,a1)=>asList(a0,a1,a0)
|
||||
// The starred element corresponds to the argument
|
||||
// deleted by the dupArgumentForm transform.
|
||||
int srcPos = ddIdx, dstPos = srcPos, dupVal = reorder[srcPos];
|
||||
boolean killFirst = false;
|
||||
for (int val; (val = reorder[--dstPos]) != dupVal; ) {
|
||||
// Set killFirst if the dup is larger than an intervening position.
|
||||
// This will remove at least one inversion from the permutation.
|
||||
if (dupVal > val) killFirst = true;
|
||||
}
|
||||
if (!killFirst) {
|
||||
srcPos = dstPos;
|
||||
dstPos = ddIdx;
|
||||
}
|
||||
form = form.editor().dupArgumentForm(1 + srcPos, 1 + dstPos);
|
||||
assert (reorder[srcPos] == reorder[dstPos]);
|
||||
oldType = oldType.dropParameterTypes(dstPos, dstPos + 1);
|
||||
// contract the reordering by removing the element at dstPos
|
||||
int tailPos = dstPos + 1;
|
||||
System.arraycopy(reorder, tailPos, reorder, dstPos, reorder.length - tailPos);
|
||||
reorder = Arrays.copyOf(reorder, reorder.length - 1);
|
||||
} else {
|
||||
int dropVal = ~ddIdx, insPos = 0;
|
||||
while (insPos < reorder.length && reorder[insPos] < dropVal) {
|
||||
// Find first element of reorder larger than dropVal.
|
||||
// This is where we will insert the dropVal.
|
||||
insPos += 1;
|
||||
}
|
||||
Class<?> ptype = newType.parameterType(dropVal);
|
||||
form = form.editor().addArgumentForm(1 + insPos, BasicType.basicType(ptype));
|
||||
oldType = oldType.insertParameterTypes(insPos, ptype);
|
||||
// expand the reordering by inserting an element at insPos
|
||||
int tailPos = insPos + 1;
|
||||
reorder = Arrays.copyOf(reorder, reorder.length + 1);
|
||||
System.arraycopy(reorder, insPos, reorder, tailPos, reorder.length - tailPos);
|
||||
reorder[insPos] = dropVal;
|
||||
}
|
||||
assert (permuteArgumentChecks(reorder, newType, oldType));
|
||||
}
|
||||
assert (reorder.length == newArity); // a perfect permutation
|
||||
// Note: This may cache too many distinct LFs. Consider backing off to varargs code.
|
||||
form = form.editor().permuteArgumentsForm(1, reorder);
|
||||
if (newType == result.type() && form == result.internalForm())
|
||||
return result;
|
||||
return result.copyWith(newType, form);
|
||||
} else {
|
||||
// first detect dropped arguments and handle them separately
|
||||
MethodHandle originalTarget = target;
|
||||
int newArity = newType.parameterCount();
|
||||
for (int dropIdx; (dropIdx = findFirstDrop(reorder, newArity)) >= 0; ) {
|
||||
// dropIdx is missing from reorder; add it in at the end
|
||||
int oldArity = reorder.length;
|
||||
target = dropArguments(target, oldArity, newType.parameterType(dropIdx));
|
||||
reorder = Arrays.copyOf(reorder, oldArity+1);
|
||||
reorder[oldArity] = dropIdx;
|
||||
}
|
||||
assert(target == originalTarget || permuteArgumentChecks(reorder, newType, target.type()));
|
||||
// Note: This may cache too many distinct LFs. Consider backing off to varargs code.
|
||||
BoundMethodHandle result = target.rebind();
|
||||
LambdaForm form = result.form.permuteArguments(1, reorder, basicTypes(newType.parameterList()));
|
||||
return result.copyWith(newType, form);
|
||||
}
|
||||
assert(target == originalTarget || permuteArgumentChecks(reorder, newType, target.type()));
|
||||
// Note: This may cache too many distinct LFs. Consider backing off to varargs code.
|
||||
BoundMethodHandle result = target.rebind();
|
||||
LambdaForm form = result.form.permuteArguments(1, reorder, basicTypes(newType.parameterList()));
|
||||
return result.copyWith(newType, form);
|
||||
}
|
||||
|
||||
/** Return the first value in [0..newArity-1] that is not present in reorder. */
|
||||
@ -2143,6 +2205,52 @@ assert((int)twice.invokeExact(21) == 42);
|
||||
return zeroPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an indication of any duplicate or omission in reorder.
|
||||
* If the reorder contains a duplicate entry, return the index of the second occurrence.
|
||||
* Otherwise, return ~(n), for the first n in [0..newArity-1] that is not present in reorder.
|
||||
* Otherwise, return zero.
|
||||
* If an element not in [0..newArity-1] is encountered, return reorder.length.
|
||||
*/
|
||||
private static int findFirstDupOrDrop(int[] reorder, int newArity) {
|
||||
final int BIT_LIMIT = 63; // max number of bits in bit mask
|
||||
if (newArity < BIT_LIMIT) {
|
||||
long mask = 0;
|
||||
for (int i = 0; i < reorder.length; i++) {
|
||||
int arg = reorder[i];
|
||||
if (arg >= newArity) return reorder.length;
|
||||
int bit = 1 << arg;
|
||||
if ((mask & bit) != 0)
|
||||
return i; // >0 indicates a dup
|
||||
mask |= bit;
|
||||
}
|
||||
if (mask == (1 << newArity) - 1) {
|
||||
assert(Long.numberOfTrailingZeros(Long.lowestOneBit(~mask)) == newArity);
|
||||
return 0;
|
||||
}
|
||||
// find first zero
|
||||
long zeroBit = Long.lowestOneBit(~mask);
|
||||
int zeroPos = Long.numberOfTrailingZeros(zeroBit);
|
||||
assert(zeroPos < newArity);
|
||||
return ~zeroPos;
|
||||
} else {
|
||||
// same algorithm, different bit set
|
||||
BitSet mask = new BitSet(newArity);
|
||||
for (int i = 0; i < reorder.length; i++) {
|
||||
int arg = reorder[i];
|
||||
if (arg >= newArity) return reorder.length;
|
||||
if (mask.get(arg))
|
||||
return i; // >0 indicates a dup
|
||||
mask.set(arg);
|
||||
}
|
||||
int zeroPos = mask.nextClearBit(0);
|
||||
if (zeroPos == newArity) {
|
||||
return 0;
|
||||
}
|
||||
return ~zeroPos;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean permuteArgumentChecks(int[] reorder, MethodType newType, MethodType oldType) {
|
||||
if (newType.returnType() != oldType.returnType())
|
||||
throw newIllegalArgumentException("return types do not match",
|
||||
@ -2374,7 +2482,14 @@ assertEquals("yz", (String) d0.invokeExact(123, "x", "y", "z"));
|
||||
if (dropped == 0) return target;
|
||||
BoundMethodHandle result = target.rebind();
|
||||
LambdaForm lform = result.form;
|
||||
lform = lform.addArguments(pos, valueTypes);
|
||||
if (USE_LAMBDA_FORM_EDITOR) {
|
||||
int insertFormArg = 1 + pos;
|
||||
for (Class<?> ptype : valueTypes) {
|
||||
lform = lform.editor().addArgumentForm(insertFormArg++, BasicType.basicType(ptype));
|
||||
}
|
||||
} else {
|
||||
lform = lform.addArguments(pos, valueTypes);
|
||||
}
|
||||
MethodType newType = oldType.insertParameterTypes(pos, valueTypes);
|
||||
result = result.copyWith(newType, lform);
|
||||
return result;
|
||||
@ -2525,7 +2640,18 @@ assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY
|
||||
/*non-public*/ static
|
||||
MethodHandle filterArgument(MethodHandle target, int pos, MethodHandle filter) {
|
||||
filterArgumentChecks(target, pos, filter);
|
||||
return MethodHandleImpl.makeCollectArguments(target, filter, pos, false);
|
||||
if (USE_LAMBDA_FORM_EDITOR) {
|
||||
MethodType targetType = target.type();
|
||||
MethodType filterType = filter.type();
|
||||
BoundMethodHandle result = target.rebind();
|
||||
Class<?> newParamType = filterType.parameterType(0);
|
||||
LambdaForm lform = result.editor().filterArgumentForm(1 + pos, BasicType.basicType(newParamType));
|
||||
MethodType newType = targetType.changeParameterType(pos, newParamType);
|
||||
result = result.copyWithExtendL(newType, lform, filter);
|
||||
return result;
|
||||
} else {
|
||||
return MethodHandleImpl.makeCollectArguments(target, filter, pos, false);
|
||||
}
|
||||
}
|
||||
|
||||
private static void filterArgumentsCheckArity(MethodHandle target, int pos, MethodHandle[] filters) {
|
||||
@ -2651,12 +2777,36 @@ assertEquals("[top, [[up, down, strange], charm], bottom]",
|
||||
*/
|
||||
public static
|
||||
MethodHandle collectArguments(MethodHandle target, int pos, MethodHandle filter) {
|
||||
MethodType newType = collectArgumentsChecks(target, pos, filter);
|
||||
if (USE_LAMBDA_FORM_EDITOR) {
|
||||
MethodType collectorType = filter.type();
|
||||
BoundMethodHandle result = target.rebind();
|
||||
LambdaForm lform;
|
||||
if (collectorType.returnType().isArray() && filter.intrinsicName() == Intrinsic.NEW_ARRAY) {
|
||||
lform = result.editor().collectArgumentArrayForm(1 + pos, filter);
|
||||
if (lform != null) {
|
||||
return result.copyWith(newType, lform);
|
||||
}
|
||||
}
|
||||
lform = result.editor().collectArgumentsForm(1 + pos, collectorType.basicType());
|
||||
return result.copyWithExtendL(newType, lform, filter);
|
||||
} else {
|
||||
return MethodHandleImpl.makeCollectArguments(target, filter, pos, false);
|
||||
}
|
||||
}
|
||||
|
||||
private static MethodType collectArgumentsChecks(MethodHandle target, int pos, MethodHandle filter) throws RuntimeException {
|
||||
MethodType targetType = target.type();
|
||||
MethodType filterType = filter.type();
|
||||
if (filterType.returnType() != void.class &&
|
||||
filterType.returnType() != targetType.parameterType(pos))
|
||||
Class<?> rtype = filterType.returnType();
|
||||
List<Class<?>> filterArgs = filterType.parameterList();
|
||||
if (rtype == void.class) {
|
||||
return targetType.insertParameterTypes(pos, filterArgs);
|
||||
}
|
||||
if (rtype != targetType.parameterType(pos)) {
|
||||
throw newIllegalArgumentException("target and filter types do not match", targetType, filterType);
|
||||
return MethodHandleImpl.makeCollectArguments(target, filter, pos, false);
|
||||
}
|
||||
return targetType.dropParameterTypes(pos, pos+1).insertParameterTypes(pos, filterArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2721,7 +2871,16 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2
|
||||
MethodType targetType = target.type();
|
||||
MethodType filterType = filter.type();
|
||||
filterReturnValueChecks(targetType, filterType);
|
||||
return MethodHandleImpl.makeCollectArguments(filter, target, 0, false);
|
||||
if (USE_LAMBDA_FORM_EDITOR) {
|
||||
BoundMethodHandle result = target.rebind();
|
||||
BasicType rtype = BasicType.basicType(filterType.returnType());
|
||||
LambdaForm lform = result.editor().filterReturnForm(rtype, false);
|
||||
MethodType newType = targetType.changeReturnType(filterType.returnType());
|
||||
result = result.copyWithExtendL(newType, lform, filter);
|
||||
return result;
|
||||
} else {
|
||||
return MethodHandleImpl.makeCollectArguments(filter, target, 0, false);
|
||||
}
|
||||
}
|
||||
|
||||
private static void filterReturnValueChecks(MethodType targetType, MethodType filterType) throws RuntimeException {
|
||||
@ -2815,7 +2974,19 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
|
||||
MethodType targetType = target.type();
|
||||
MethodType combinerType = combiner.type();
|
||||
Class<?> rtype = foldArgumentChecks(foldPos, targetType, combinerType);
|
||||
return MethodHandleImpl.makeCollectArguments(target, combiner, foldPos, true);
|
||||
if (USE_LAMBDA_FORM_EDITOR) {
|
||||
BoundMethodHandle result = target.rebind();
|
||||
boolean dropResult = (rtype == void.class);
|
||||
// Note: This may cache too many distinct LFs. Consider backing off to varargs code.
|
||||
LambdaForm lform = result.editor().foldArgumentsForm(1 + foldPos, dropResult, combinerType.basicType());
|
||||
MethodType newType = targetType;
|
||||
if (!dropResult)
|
||||
newType = newType.dropParameterTypes(foldPos, foldPos + 1);
|
||||
result = result.copyWithExtendL(newType, lform, combiner);
|
||||
return result;
|
||||
} else {
|
||||
return MethodHandleImpl.makeCollectArguments(target, combiner, foldPos, true);
|
||||
}
|
||||
}
|
||||
|
||||
private static Class<?> foldArgumentChecks(int foldPos, MethodType targetType, MethodType combinerType) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user