8213478: Reduce rebinds when applying repeated filters and conversions
Reviewed-by: vlivanov, jrose
This commit is contained in:
parent
2df435e191
commit
eda5f09014
@ -30,6 +30,8 @@ import sun.invoke.util.Wrapper;
|
|||||||
import java.lang.ref.SoftReference;
|
import java.lang.ref.SoftReference;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.TreeMap;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import static java.lang.invoke.LambdaForm.*;
|
import static java.lang.invoke.LambdaForm.*;
|
||||||
@ -86,7 +88,8 @@ class LambdaFormEditor {
|
|||||||
LOCAL_TYPES = 14,
|
LOCAL_TYPES = 14,
|
||||||
FOLD_SELECT_ARGS = 15,
|
FOLD_SELECT_ARGS = 15,
|
||||||
FOLD_SELECT_ARGS_TO_VOID = 16,
|
FOLD_SELECT_ARGS_TO_VOID = 16,
|
||||||
FILTER_SELECT_ARGS = 17;
|
FILTER_SELECT_ARGS = 17,
|
||||||
|
REPEAT_FILTER_ARGS = 18;
|
||||||
|
|
||||||
private static final boolean STRESS_TEST = false; // turn on to disable most packing
|
private static final boolean STRESS_TEST = false; // turn on to disable most packing
|
||||||
private static final int
|
private static final int
|
||||||
@ -641,6 +644,104 @@ class LambdaFormEditor {
|
|||||||
return putInCache(key, form);
|
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);
|
||||||
|
byte[] keyArgs = new byte[argPositions.length + 2];
|
||||||
|
keyArgs[0] = Transform.REPEAT_FILTER_ARGS;
|
||||||
|
keyArgs[argPositions.length + 1] = (byte)newType.ordinal();
|
||||||
|
for (int i = 0; i < argPositions.length; i++) {
|
||||||
|
keyArgs[i + 1] = (byte)argPositions[i];
|
||||||
|
}
|
||||||
|
Transform key = new Transform(keyArgs);
|
||||||
|
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,
|
private LambdaForm makeArgumentCombinationForm(int pos,
|
||||||
MethodType combinerType,
|
MethodType combinerType,
|
||||||
boolean keepArguments, boolean dropResult) {
|
boolean keepArguments, boolean dropResult) {
|
||||||
|
@ -42,6 +42,7 @@ import sun.invoke.util.Wrapper;
|
|||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -257,14 +258,19 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
|||||||
|
|
||||||
private static int countNonNull(Object[] array) {
|
private static int countNonNull(Object[] array) {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (Object x : array) {
|
if (array != null) {
|
||||||
if (x != null) ++count;
|
for (Object x : array) {
|
||||||
|
if (x != null) ++count;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
static MethodHandle makePairwiseConvertByEditor(MethodHandle target, MethodType srcType,
|
static MethodHandle makePairwiseConvertByEditor(MethodHandle target, MethodType srcType,
|
||||||
boolean strict, boolean monobox) {
|
boolean strict, boolean monobox) {
|
||||||
|
// In method types arguments start at index 0, while the LF
|
||||||
|
// editor have the MH receiver at position 0 - adjust appropriately.
|
||||||
|
final int MH_RECEIVER_OFFSET = 1;
|
||||||
Object[] convSpecs = computeValueConversions(srcType, target.type(), strict, monobox);
|
Object[] convSpecs = computeValueConversions(srcType, target.type(), strict, monobox);
|
||||||
int convCount = countNonNull(convSpecs);
|
int convCount = countNonNull(convSpecs);
|
||||||
if (convCount == 0)
|
if (convCount == 0)
|
||||||
@ -272,27 +278,52 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
|||||||
MethodType basicSrcType = srcType.basicType();
|
MethodType basicSrcType = srcType.basicType();
|
||||||
MethodType midType = target.type().basicType();
|
MethodType midType = target.type().basicType();
|
||||||
BoundMethodHandle mh = target.rebind();
|
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.
|
// Match each unique conversion to the positions at which it is to be applied
|
||||||
for (int i = 0; i < convSpecs.length-1; i++) {
|
var convSpecMap = new HashMap<Object, int[]>(((4 * convCount) / 3) + 1);
|
||||||
|
for (int i = 0; i < convSpecs.length - MH_RECEIVER_OFFSET; i++) {
|
||||||
Object convSpec = convSpecs[i];
|
Object convSpec = convSpecs[i];
|
||||||
if (convSpec == null) continue;
|
if (convSpec == null) continue;
|
||||||
|
int[] positions = convSpecMap.get(convSpec);
|
||||||
|
if (positions == null) {
|
||||||
|
positions = new int[] { i + MH_RECEIVER_OFFSET };
|
||||||
|
} else {
|
||||||
|
positions = Arrays.copyOf(positions, positions.length + 1);
|
||||||
|
positions[positions.length - 1] = i + MH_RECEIVER_OFFSET;
|
||||||
|
}
|
||||||
|
convSpecMap.put(convSpec, positions);
|
||||||
|
}
|
||||||
|
for (var entry : convSpecMap.entrySet()) {
|
||||||
|
Object convSpec = entry.getKey();
|
||||||
|
|
||||||
MethodHandle fn;
|
MethodHandle fn;
|
||||||
if (convSpec instanceof Class) {
|
if (convSpec instanceof Class) {
|
||||||
fn = getConstantHandle(MH_cast).bindTo(convSpec);
|
fn = getConstantHandle(MH_cast).bindTo(convSpec);
|
||||||
} else {
|
} else {
|
||||||
fn = (MethodHandle) convSpec;
|
fn = (MethodHandle) convSpec;
|
||||||
}
|
}
|
||||||
Class<?> newType = basicSrcType.parameterType(i);
|
int[] positions = entry.getValue();
|
||||||
if (--convCount == 0)
|
Class<?> newType = basicSrcType.parameterType(positions[0] - MH_RECEIVER_OFFSET);
|
||||||
|
BasicType newBasicType = BasicType.basicType(newType);
|
||||||
|
convCount -= positions.length;
|
||||||
|
if (convCount == 0) {
|
||||||
midType = srcType;
|
midType = srcType;
|
||||||
else
|
} else {
|
||||||
midType = midType.changeParameterType(i, newType);
|
Class<?>[] ptypes = midType.ptypes().clone();
|
||||||
LambdaForm form2 = mh.editor().filterArgumentForm(1+i, BasicType.basicType(newType));
|
for (int pos : positions) {
|
||||||
|
ptypes[pos - 1] = newType;
|
||||||
|
}
|
||||||
|
midType = MethodType.makeImpl(midType.rtype(), ptypes, true);
|
||||||
|
}
|
||||||
|
LambdaForm form2;
|
||||||
|
if (positions.length > 1) {
|
||||||
|
form2 = mh.editor().filterRepeatedArgumentForm(newBasicType, positions);
|
||||||
|
} else {
|
||||||
|
form2 = mh.editor().filterArgumentForm(positions[0], newBasicType);
|
||||||
|
}
|
||||||
mh = mh.copyWithExtendL(midType, form2, fn);
|
mh = mh.copyWithExtendL(midType, form2, fn);
|
||||||
mh = mh.rebind();
|
|
||||||
}
|
}
|
||||||
Object convSpec = convSpecs[convSpecs.length-1];
|
Object convSpec = convSpecs[convSpecs.length - 1];
|
||||||
if (convSpec != null) {
|
if (convSpec != null) {
|
||||||
MethodHandle fn;
|
MethodHandle fn;
|
||||||
if (convSpec instanceof Class) {
|
if (convSpec instanceof Class) {
|
||||||
@ -320,98 +351,18 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
|||||||
return mh;
|
return mh;
|
||||||
}
|
}
|
||||||
|
|
||||||
static MethodHandle makePairwiseConvertIndirect(MethodHandle target, MethodType srcType,
|
|
||||||
boolean strict, boolean monobox) {
|
|
||||||
assert(target.type().parameterCount() == srcType.parameterCount());
|
|
||||||
// Calculate extra arguments (temporaries) required in the names array.
|
|
||||||
Object[] convSpecs = computeValueConversions(srcType, target.type(), strict, monobox);
|
|
||||||
final int INARG_COUNT = srcType.parameterCount();
|
|
||||||
int convCount = countNonNull(convSpecs);
|
|
||||||
boolean retConv = (convSpecs[INARG_COUNT] != null);
|
|
||||||
boolean retVoid = srcType.returnType() == void.class;
|
|
||||||
if (retConv && retVoid) {
|
|
||||||
convCount -= 1;
|
|
||||||
retConv = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final int IN_MH = 0;
|
|
||||||
final int INARG_BASE = 1;
|
|
||||||
final int INARG_LIMIT = INARG_BASE + INARG_COUNT;
|
|
||||||
final int NAME_LIMIT = INARG_LIMIT + convCount + 1;
|
|
||||||
final int RETURN_CONV = (!retConv ? -1 : NAME_LIMIT - 1);
|
|
||||||
final int OUT_CALL = (!retConv ? NAME_LIMIT : RETURN_CONV) - 1;
|
|
||||||
final int RESULT = (retVoid ? -1 : NAME_LIMIT - 1);
|
|
||||||
|
|
||||||
// Now build a LambdaForm.
|
|
||||||
MethodType lambdaType = srcType.basicType().invokerType();
|
|
||||||
Name[] names = arguments(NAME_LIMIT - INARG_LIMIT, lambdaType);
|
|
||||||
|
|
||||||
// Collect the arguments to the outgoing call, maybe with conversions:
|
|
||||||
final int OUTARG_BASE = 0; // target MH is Name.function, name Name.arguments[0]
|
|
||||||
Object[] outArgs = new Object[OUTARG_BASE + INARG_COUNT];
|
|
||||||
|
|
||||||
int nameCursor = INARG_LIMIT;
|
|
||||||
for (int i = 0; i < INARG_COUNT; i++) {
|
|
||||||
Object convSpec = convSpecs[i];
|
|
||||||
if (convSpec == null) {
|
|
||||||
// do nothing: difference is trivial
|
|
||||||
outArgs[OUTARG_BASE + i] = names[INARG_BASE + i];
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Name conv;
|
|
||||||
if (convSpec instanceof Class) {
|
|
||||||
Class<?> convClass = (Class<?>) convSpec;
|
|
||||||
conv = new Name(getConstantHandle(MH_cast), convClass, names[INARG_BASE + i]);
|
|
||||||
} else {
|
|
||||||
MethodHandle fn = (MethodHandle) convSpec;
|
|
||||||
conv = new Name(fn, names[INARG_BASE + i]);
|
|
||||||
}
|
|
||||||
assert(names[nameCursor] == null);
|
|
||||||
names[nameCursor++] = conv;
|
|
||||||
assert(outArgs[OUTARG_BASE + i] == null);
|
|
||||||
outArgs[OUTARG_BASE + i] = conv;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build argument array for the call.
|
|
||||||
assert(nameCursor == OUT_CALL);
|
|
||||||
names[OUT_CALL] = new Name(target, outArgs);
|
|
||||||
|
|
||||||
Object convSpec = convSpecs[INARG_COUNT];
|
|
||||||
if (!retConv) {
|
|
||||||
assert(OUT_CALL == names.length-1);
|
|
||||||
} else {
|
|
||||||
Name conv;
|
|
||||||
if (convSpec == void.class) {
|
|
||||||
conv = new Name(LambdaForm.constantZero(BasicType.basicType(srcType.returnType())));
|
|
||||||
} else if (convSpec instanceof Class) {
|
|
||||||
Class<?> convClass = (Class<?>) convSpec;
|
|
||||||
conv = new Name(getConstantHandle(MH_cast), convClass, names[OUT_CALL]);
|
|
||||||
} else {
|
|
||||||
MethodHandle fn = (MethodHandle) convSpec;
|
|
||||||
if (fn.type().parameterCount() == 0)
|
|
||||||
conv = new Name(fn); // don't pass retval to void conversion
|
|
||||||
else
|
|
||||||
conv = new Name(fn, names[OUT_CALL]);
|
|
||||||
}
|
|
||||||
assert(names[RETURN_CONV] == null);
|
|
||||||
names[RETURN_CONV] = conv;
|
|
||||||
assert(RETURN_CONV == names.length-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
LambdaForm form = new LambdaForm(lambdaType.parameterCount(), names, RESULT, Kind.CONVERT);
|
|
||||||
return SimpleMethodHandle.make(srcType, form);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Object[] computeValueConversions(MethodType srcType, MethodType dstType,
|
static Object[] computeValueConversions(MethodType srcType, MethodType dstType,
|
||||||
boolean strict, boolean monobox) {
|
boolean strict, boolean monobox) {
|
||||||
final int INARG_COUNT = srcType.parameterCount();
|
final int INARG_COUNT = srcType.parameterCount();
|
||||||
Object[] convSpecs = new Object[INARG_COUNT+1];
|
Object[] convSpecs = null;
|
||||||
for (int i = 0; i <= INARG_COUNT; i++) {
|
for (int i = 0; i <= INARG_COUNT; i++) {
|
||||||
boolean isRet = (i == INARG_COUNT);
|
boolean isRet = (i == INARG_COUNT);
|
||||||
Class<?> src = isRet ? dstType.returnType() : srcType.parameterType(i);
|
Class<?> src = isRet ? dstType.returnType() : srcType.parameterType(i);
|
||||||
Class<?> dst = isRet ? srcType.returnType() : dstType.parameterType(i);
|
Class<?> dst = isRet ? srcType.returnType() : dstType.parameterType(i);
|
||||||
if (!VerifyType.isNullConversion(src, dst, /*keepInterfaces=*/ strict)) {
|
if (!VerifyType.isNullConversion(src, dst, /*keepInterfaces=*/ strict)) {
|
||||||
|
if (convSpecs == null) {
|
||||||
|
convSpecs = new Object[INARG_COUNT + 1];
|
||||||
|
}
|
||||||
convSpecs[i] = valueConversion(src, dst, strict, monobox);
|
convSpecs[i] = valueConversion(src, dst, strict, monobox);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3864,18 +3864,63 @@ assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY
|
|||||||
*/
|
*/
|
||||||
public static
|
public static
|
||||||
MethodHandle filterArguments(MethodHandle target, int pos, MethodHandle... filters) {
|
MethodHandle filterArguments(MethodHandle target, int pos, MethodHandle... filters) {
|
||||||
|
// In method types arguments start at index 0, while the LF
|
||||||
|
// editor have the MH receiver at position 0 - adjust appropriately.
|
||||||
|
final int MH_RECEIVER_OFFSET = 1;
|
||||||
filterArgumentsCheckArity(target, pos, filters);
|
filterArgumentsCheckArity(target, pos, filters);
|
||||||
MethodHandle adapter = target;
|
MethodHandle adapter = target;
|
||||||
|
|
||||||
|
// keep track of currently matched filters, as to optimize repeated filters
|
||||||
|
int index = 0;
|
||||||
|
int[] positions = new int[filters.length];
|
||||||
|
MethodHandle filter = null;
|
||||||
|
|
||||||
// process filters in reverse order so that the invocation of
|
// process filters in reverse order so that the invocation of
|
||||||
// the resulting adapter will invoke the filters in left-to-right order
|
// the resulting adapter will invoke the filters in left-to-right order
|
||||||
for (int i = filters.length - 1; i >= 0; --i) {
|
for (int i = filters.length - 1; i >= 0; --i) {
|
||||||
MethodHandle filter = filters[i];
|
MethodHandle newFilter = filters[i];
|
||||||
if (filter == null) continue; // ignore null elements of filters
|
if (newFilter == null) continue; // ignore null elements of filters
|
||||||
adapter = filterArgument(adapter, pos + i, filter);
|
|
||||||
|
// flush changes on update
|
||||||
|
if (filter != newFilter) {
|
||||||
|
if (filter != null) {
|
||||||
|
if (index > 1) {
|
||||||
|
adapter = filterRepeatedArgument(adapter, filter, Arrays.copyOf(positions, index));
|
||||||
|
} else {
|
||||||
|
adapter = filterArgument(adapter, positions[0] - 1, filter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
filter = newFilter;
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
filterArgumentChecks(target, pos + i, newFilter);
|
||||||
|
positions[index++] = pos + i + MH_RECEIVER_OFFSET;
|
||||||
|
}
|
||||||
|
if (index > 1) {
|
||||||
|
adapter = filterRepeatedArgument(adapter, filter, Arrays.copyOf(positions, index));
|
||||||
|
} else if (index == 1) {
|
||||||
|
adapter = filterArgument(adapter, positions[0] - 1, filter);
|
||||||
}
|
}
|
||||||
return adapter;
|
return adapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static MethodHandle filterRepeatedArgument(MethodHandle adapter, MethodHandle filter, int[] positions) {
|
||||||
|
MethodType targetType = adapter.type();
|
||||||
|
MethodType filterType = filter.type();
|
||||||
|
BoundMethodHandle result = adapter.rebind();
|
||||||
|
Class<?> newParamType = filterType.parameterType(0);
|
||||||
|
|
||||||
|
Class<?>[] ptypes = targetType.ptypes().clone();
|
||||||
|
for (int pos : positions) {
|
||||||
|
ptypes[pos - 1] = newParamType;
|
||||||
|
}
|
||||||
|
MethodType newType = MethodType.makeImpl(targetType.rtype(), ptypes, true);
|
||||||
|
|
||||||
|
LambdaForm lform = result.editor().filterRepeatedArgumentForm(BasicType.basicType(newParamType), positions);
|
||||||
|
return result.copyWithExtendL(newType, lform, filter);
|
||||||
|
}
|
||||||
|
|
||||||
/*non-public*/ static
|
/*non-public*/ static
|
||||||
MethodHandle filterArgument(MethodHandle target, int pos, MethodHandle filter) {
|
MethodHandle filterArgument(MethodHandle target, int pos, MethodHandle filter) {
|
||||||
filterArgumentChecks(target, pos, filter);
|
filterArgumentChecks(target, pos, filter);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user