8264288: Performance issue with MethodHandle.asCollector

Reviewed-by: jrose, vlivanov
This commit is contained in:
Jorn Vernee 2021-04-05 20:05:07 +00:00
parent 920189918e
commit b7baca7f32
10 changed files with 197 additions and 358 deletions

@ -869,13 +869,6 @@ class InvokerBytecodeGenerator {
onStack = emitLoop(i);
i += 2; // jump to the end of the LOOP idiom
continue;
case NEW_ARRAY:
Class<?> rtype = name.function.methodType().returnType();
if (isStaticallyNameable(rtype)) {
emitNewArray(name);
continue;
}
break;
case ARRAY_LOAD:
emitArrayLoad(name);
continue;
@ -1112,43 +1105,6 @@ class InvokerBytecodeGenerator {
}
}
void emitNewArray(Name name) throws InternalError {
Class<?> rtype = name.function.methodType().returnType();
if (name.arguments.length == 0) {
// The array will be a constant.
Object emptyArray;
try {
emptyArray = name.function.resolvedHandle().invoke();
} catch (Throwable ex) {
throw uncaughtException(ex);
}
assert(java.lang.reflect.Array.getLength(emptyArray) == 0);
assert(emptyArray.getClass() == rtype); // exact typing
mv.visitFieldInsn(Opcodes.GETSTATIC, className, classData(emptyArray), "Ljava/lang/Object;");
emitReferenceCast(rtype, emptyArray);
return;
}
Class<?> arrayElementType = rtype.getComponentType();
assert(arrayElementType != null);
emitIconstInsn(name.arguments.length);
int xas = Opcodes.AASTORE;
if (!arrayElementType.isPrimitive()) {
mv.visitTypeInsn(Opcodes.ANEWARRAY, getInternalName(arrayElementType));
} else {
byte tc = arrayTypeCode(Wrapper.forPrimitiveType(arrayElementType));
xas = arrayInsnOpcode(tc, xas);
mv.visitIntInsn(Opcodes.NEWARRAY, tc);
}
// store arguments
for (int i = 0; i < name.arguments.length; i++) {
mv.visitInsn(Opcodes.DUP);
emitIconstInsn(i);
emitPushArgument(name, i);
mv.visitInsn(xas);
}
// the array is left on the stack
assertStaticType(rtype, name);
}
int refKindOpcode(byte refKind) {
switch (refKind) {
case REF_invokeVirtual: return Opcodes.INVOKEVIRTUAL;

@ -315,6 +315,7 @@ class LambdaForm {
PUT_DOUBLE_VOLATILE("putDoubleVolatile"),
TRY_FINALLY("tryFinally"),
COLLECT("collect"),
COLLECTOR("collector"),
CONVERT("convert"),
SPREAD("spread"),
LOOP("loop"),

@ -648,57 +648,6 @@ class LambdaFormEditor {
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)));
byte kind = COLLECT_ARGS_TO_ARRAY;
TransformKey key = TransformKey.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(new NamedFunction(arrayCollector, Intrinsic.NEW_ARRAY),
(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) {
TransformKey key = TransformKey.of(FILTER_ARG, pos, newType.ordinal());
LambdaForm form = getInCache(key);

@ -1244,13 +1244,9 @@ assertEquals("[123]", (String) longsToString.invokeExact((long)123));
asCollectorChecks(arrayType, collectArgPos, arrayLength);
BoundMethodHandle mh = rebind();
MethodType resultType = type().asCollectorType(arrayType, collectArgPos, 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);
MethodHandle collector = MethodHandleImpl.varargsArray(arrayType, arrayLength);
LambdaForm lform = mh.editor().collectArgumentsForm(1 + collectArgPos, collector.type().basicType());
return mh.copyWithExtendL(resultType, lform, collector);
}
/**

@ -1227,7 +1227,6 @@ abstract class MethodHandleImpl {
GUARD_WITH_CATCH,
TRY_FINALLY,
LOOP,
NEW_ARRAY,
ARRAY_LOAD,
ARRAY_STORE,
ARRAY_LENGTH,
@ -1292,120 +1291,8 @@ abstract class MethodHandleImpl {
return new IntrinsicMethodHandle(SimpleMethodHandle.make(type, form), intrinsicName);
}
/// Collection of multiple arguments.
private static MethodHandle findCollector(String name, int nargs, Class<?> rtype, Class<?>... ptypes) {
MethodType type = MethodType.genericMethodType(nargs)
.changeReturnType(rtype)
.insertParameterTypes(0, ptypes);
try {
return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, name, type);
} catch (ReflectiveOperationException ex) {
return null;
}
}
private static final Object[] NO_ARGS_ARRAY = {};
private static Object[] makeArray(Object... args) { return args; }
private static Object[] array() { return NO_ARGS_ARRAY; }
private static Object[] array(Object a0)
{ return makeArray(a0); }
private static Object[] array(Object a0, Object a1)
{ return makeArray(a0, a1); }
private static Object[] array(Object a0, Object a1, Object a2)
{ return makeArray(a0, a1, a2); }
private static Object[] array(Object a0, Object a1, Object a2, Object a3)
{ return makeArray(a0, a1, a2, a3); }
private static Object[] array(Object a0, Object a1, Object a2, Object a3,
Object a4)
{ return makeArray(a0, a1, a2, a3, a4); }
private static Object[] array(Object a0, Object a1, Object a2, Object a3,
Object a4, Object a5)
{ return makeArray(a0, a1, a2, a3, a4, a5); }
private static Object[] array(Object a0, Object a1, Object a2, Object a3,
Object a4, Object a5, Object a6)
{ return makeArray(a0, a1, a2, a3, a4, a5, a6); }
private static Object[] array(Object a0, Object a1, Object a2, Object a3,
Object a4, Object a5, Object a6, Object a7)
{ return makeArray(a0, a1, a2, a3, a4, a5, a6, a7); }
private static Object[] array(Object a0, Object a1, Object a2, Object a3,
Object a4, Object a5, Object a6, Object a7,
Object a8)
{ return makeArray(a0, a1, a2, a3, a4, a5, a6, a7, a8); }
private static Object[] array(Object a0, Object a1, Object a2, Object a3,
Object a4, Object a5, Object a6, Object a7,
Object a8, Object a9)
{ return makeArray(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); }
private static final int ARRAYS_COUNT = 11;
private static final @Stable MethodHandle[] ARRAYS = new MethodHandle[MAX_ARITY + 1];
// filling versions of the above:
// using Integer len instead of int len and no varargs to avoid bootstrapping problems
private static Object[] fillNewArray(Integer len, Object[] /*not ...*/ args) {
Object[] a = new Object[len];
fillWithArguments(a, 0, args);
return a;
}
private static Object[] fillNewTypedArray(Object[] example, Integer len, Object[] /*not ...*/ args) {
Object[] a = Arrays.copyOf(example, len);
assert(a.getClass() != Object[].class);
fillWithArguments(a, 0, args);
return a;
}
private static void fillWithArguments(Object[] a, int pos, Object... args) {
System.arraycopy(args, 0, a, pos, args.length);
}
// using Integer pos instead of int pos to avoid bootstrapping problems
private static Object[] fillArray(Integer pos, Object[] a, Object a0)
{ fillWithArguments(a, pos, a0); return a; }
private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1)
{ fillWithArguments(a, pos, a0, a1); return a; }
private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2)
{ fillWithArguments(a, pos, a0, a1, a2); return a; }
private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3)
{ fillWithArguments(a, pos, a0, a1, a2, a3); return a; }
private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3,
Object a4)
{ fillWithArguments(a, pos, a0, a1, a2, a3, a4); return a; }
private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3,
Object a4, Object a5)
{ fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5); return a; }
private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3,
Object a4, Object a5, Object a6)
{ fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6); return a; }
private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3,
Object a4, Object a5, Object a6, Object a7)
{ fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7); return a; }
private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3,
Object a4, Object a5, Object a6, Object a7,
Object a8)
{ fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7, a8); return a; }
private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3,
Object a4, Object a5, Object a6, Object a7,
Object a8, Object a9)
{ fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); return a; }
private static final int FILL_ARRAYS_COUNT = 11; // current number of fillArray methods
private static final @Stable MethodHandle[] FILL_ARRAYS = new MethodHandle[FILL_ARRAYS_COUNT];
private static MethodHandle getFillArray(int count) {
assert (count > 0 && count < FILL_ARRAYS_COUNT);
MethodHandle mh = FILL_ARRAYS[count];
if (mh != null) {
return mh;
}
mh = findCollector("fillArray", count, Object[].class, Integer.class, Object[].class);
FILL_ARRAYS[count] = mh;
return mh;
}
private static Object copyAsPrimitiveArray(Wrapper w, Object... boxes) {
Object a = w.makeArray(boxes.length);
w.copyArrayUnboxing(boxes, 0, a, 0, boxes.length);
return a;
}
/** Return a method handle that takes the indicated number of Object
* arguments and returns an Object array of them, as if for varargs.
*/
@ -1414,97 +1301,11 @@ abstract class MethodHandleImpl {
if (mh != null) {
return mh;
}
if (nargs < ARRAYS_COUNT) {
mh = findCollector("array", nargs, Object[].class);
} else {
mh = buildVarargsArray(getConstantHandle(MH_fillNewArray),
getConstantHandle(MH_arrayIdentity), nargs);
}
mh = makeCollector(Object[].class, nargs);
assert(assertCorrectArity(mh, nargs));
mh = makeIntrinsic(mh, Intrinsic.NEW_ARRAY);
return ARRAYS[nargs] = mh;
}
private static boolean assertCorrectArity(MethodHandle mh, int arity) {
assert(mh.type().parameterCount() == arity) : "arity != "+arity+": "+mh;
return true;
}
// Array identity function (used as getConstantHandle(MH_arrayIdentity)).
static <T> T[] identity(T[] x) {
return x;
}
private static MethodHandle buildVarargsArray(MethodHandle newArray, MethodHandle finisher, int nargs) {
// Build up the result mh as a sequence of fills like this:
// finisher(fill(fill(newArrayWA(23,x1..x10),10,x11..x20),20,x21..x23))
// The various fill(_,10*I,___*[J]) are reusable.
int leftLen = Math.min(nargs, LEFT_ARGS); // absorb some arguments immediately
int rightLen = nargs - leftLen;
MethodHandle leftCollector = newArray.bindTo(nargs);
leftCollector = leftCollector.asCollector(Object[].class, leftLen);
MethodHandle mh = finisher;
if (rightLen > 0) {
MethodHandle rightFiller = fillToRight(LEFT_ARGS + rightLen);
if (mh.equals(getConstantHandle(MH_arrayIdentity)))
mh = rightFiller;
else
mh = MethodHandles.collectArguments(mh, 0, rightFiller);
}
if (mh.equals(getConstantHandle(MH_arrayIdentity)))
mh = leftCollector;
else
mh = MethodHandles.collectArguments(mh, 0, leftCollector);
return mh;
}
private static final int LEFT_ARGS = FILL_ARRAYS_COUNT - 1;
private static final @Stable MethodHandle[] FILL_ARRAY_TO_RIGHT = new MethodHandle[MAX_ARITY + 1];
/** fill_array_to_right(N).invoke(a, argL..arg[N-1])
* fills a[L]..a[N-1] with corresponding arguments,
* and then returns a. The value L is a global constant (LEFT_ARGS).
*/
private static MethodHandle fillToRight(int nargs) {
MethodHandle filler = FILL_ARRAY_TO_RIGHT[nargs];
if (filler != null) return filler;
filler = buildFiller(nargs);
assert(assertCorrectArity(filler, nargs - LEFT_ARGS + 1));
return FILL_ARRAY_TO_RIGHT[nargs] = filler;
}
private static MethodHandle buildFiller(int nargs) {
if (nargs <= LEFT_ARGS)
return getConstantHandle(MH_arrayIdentity); // no args to fill; return the array unchanged
// we need room for both mh and a in mh.invoke(a, arg*[nargs])
final int CHUNK = LEFT_ARGS;
int rightLen = nargs % CHUNK;
int midLen = nargs - rightLen;
if (rightLen == 0) {
midLen = nargs - (rightLen = CHUNK);
if (FILL_ARRAY_TO_RIGHT[midLen] == null) {
// build some precursors from left to right
for (int j = LEFT_ARGS % CHUNK; j < midLen; j += CHUNK)
if (j > LEFT_ARGS) fillToRight(j);
}
}
if (midLen < LEFT_ARGS) rightLen = nargs - (midLen = LEFT_ARGS);
assert(rightLen > 0);
MethodHandle midFill = fillToRight(midLen); // recursive fill
MethodHandle rightFill = getFillArray(rightLen).bindTo(midLen); // [midLen..nargs-1]
assert(midFill.type().parameterCount() == 1 + midLen - LEFT_ARGS);
assert(rightFill.type().parameterCount() == 1 + rightLen);
// Combine the two fills:
// right(mid(a, x10..x19), x20..x23)
// The final product will look like this:
// right(mid(newArrayLeft(24, x0..x9), x10..x19), x20..x23)
if (midLen == LEFT_ARGS)
return rightFill;
else
return MethodHandles.collectArguments(rightFill, 0, midFill);
}
static final int MAX_JVM_ARITY = 255; // limit imposed by the JVM
/** Return a method handle that takes the indicated number of
* typed arguments and returns an array of them.
* The type argument is the array type.
@ -1512,7 +1313,6 @@ abstract class MethodHandleImpl {
static MethodHandle varargsArray(Class<?> arrayType, int nargs) {
Class<?> elemType = arrayType.getComponentType();
if (elemType == null) throw new IllegalArgumentException("not an array: "+arrayType);
// FIXME: Need more special casing and caching here.
if (nargs >= MAX_JVM_ARITY/2 - 1) {
int slots = nargs;
final int MAX_ARRAY_SLOTS = MAX_JVM_ARITY - 1; // 1 for receiver MH
@ -1527,34 +1327,20 @@ abstract class MethodHandleImpl {
MethodHandle cache[] = Makers.TYPED_COLLECTORS.get(elemType);
MethodHandle mh = nargs < cache.length ? cache[nargs] : null;
if (mh != null) return mh;
if (nargs == 0) {
Object example = java.lang.reflect.Array.newInstance(arrayType.getComponentType(), 0);
mh = MethodHandles.constant(arrayType, example);
} else if (elemType.isPrimitive()) {
MethodHandle builder = getConstantHandle(MH_fillNewArray);
MethodHandle producer = buildArrayProducer(arrayType);
mh = buildVarargsArray(builder, producer, nargs);
} else {
Class<? extends Object[]> objArrayType = arrayType.asSubclass(Object[].class);
Object[] example = Arrays.copyOf(NO_ARGS_ARRAY, 0, objArrayType);
MethodHandle builder = getConstantHandle(MH_fillNewTypedArray).bindTo(example);
MethodHandle producer = getConstantHandle(MH_arrayIdentity); // must be weakly typed
mh = buildVarargsArray(builder, producer, nargs);
}
mh = mh.asType(MethodType.methodType(arrayType, Collections.<Class<?>>nCopies(nargs, elemType)));
mh = makeIntrinsic(mh, Intrinsic.NEW_ARRAY);
mh = makeCollector(arrayType, nargs);
assert(assertCorrectArity(mh, nargs));
if (nargs < cache.length)
cache[nargs] = mh;
return mh;
}
private static MethodHandle buildArrayProducer(Class<?> arrayType) {
Class<?> elemType = arrayType.getComponentType();
assert(elemType.isPrimitive());
return getConstantHandle(MH_copyAsPrimitiveArray).bindTo(Wrapper.forPrimitiveType(elemType));
private static boolean assertCorrectArity(MethodHandle mh, int arity) {
assert(mh.type().parameterCount() == arity) : "arity != "+arity+": "+mh;
return true;
}
static final int MAX_JVM_ARITY = 255; // limit imposed by the JVM
/*non-public*/
static void assertSame(Object mh1, Object mh2) {
if (mh1 != mh2) {
@ -2089,21 +1875,94 @@ abstract class MethodHandleImpl {
return r;
}
// see varargsArray method for chaching/package-private version of this
private static MethodHandle makeCollector(Class<?> arrayType, int parameterCount) {
MethodType type = MethodType.methodType(arrayType, Collections.nCopies(parameterCount, arrayType.componentType()));
MethodHandle newArray = MethodHandles.arrayConstructor(arrayType);
LambdaForm form = makeCollectorForm(type.basicType(), arrayType);
BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_L();
BoundMethodHandle mh;
try {
mh = (BoundMethodHandle) data.factory().invokeBasic(type, form, (Object) newArray);
} catch (Throwable ex) {
throw uncaughtException(ex);
}
assert(mh.type() == type);
return mh;
}
private static LambdaForm makeCollectorForm(MethodType basicType, Class<?> arrayType) {
MethodType lambdaType = basicType.invokerType();
int parameterCount = basicType.parameterCount();
// Only share the lambda form for empty arrays and reference types.
// Sharing based on the basic type alone doesn't work because
// we need a separate lambda form for byte/short/char/int which
// are all erased to int otherwise.
// Other caching for primitive types happens at the MethodHandle level (see varargsArray).
boolean isReferenceType = !arrayType.componentType().isPrimitive();
boolean isSharedLambdaForm = parameterCount == 0 || isReferenceType;
if (isSharedLambdaForm) {
LambdaForm lform = basicType.form().cachedLambdaForm(MethodTypeForm.LF_COLLECTOR);
if (lform != null) {
return lform;
}
}
// use erased accessor for reference types
MethodHandle storeFunc = isReferenceType
? ArrayAccessor.OBJECT_ARRAY_SETTER
: makeArrayElementAccessor(arrayType, ArrayAccess.SET);
final int THIS_MH = 0; // the BMH_L
final int ARG_BASE = 1; // start of incoming arguments
final int ARG_LIMIT = ARG_BASE + parameterCount;
int nameCursor = ARG_LIMIT;
final int GET_NEW_ARRAY = nameCursor++;
final int CALL_NEW_ARRAY = nameCursor++;
final int STORE_ELEMENT_BASE = nameCursor;
final int STORE_ELEMENT_LIMIT = STORE_ELEMENT_BASE + parameterCount;
nameCursor = STORE_ELEMENT_LIMIT;
Name[] names = arguments(nameCursor - ARG_LIMIT, lambdaType);
BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_L();
names[THIS_MH] = names[THIS_MH].withConstraint(data);
names[GET_NEW_ARRAY] = new Name(data.getterFunction(0), names[THIS_MH]);
MethodHandle invokeBasic = MethodHandles.basicInvoker(MethodType.methodType(Object.class, int.class));
names[CALL_NEW_ARRAY] = new Name(new NamedFunction(invokeBasic), names[GET_NEW_ARRAY], parameterCount);
for (int storeIndex = 0,
storeNameCursor = STORE_ELEMENT_BASE,
argCursor = ARG_BASE;
storeNameCursor < STORE_ELEMENT_LIMIT;
storeIndex++, storeNameCursor++, argCursor++){
names[storeNameCursor] = new Name(new NamedFunction(storeFunc, Intrinsic.ARRAY_STORE),
names[CALL_NEW_ARRAY], storeIndex, names[argCursor]);
}
LambdaForm lform = new LambdaForm(lambdaType.parameterCount(), names, CALL_NEW_ARRAY, Kind.COLLECTOR);
if (isSharedLambdaForm) {
lform = basicType.form().setCachedLambdaForm(MethodTypeForm.LF_COLLECTOR, lform);
}
return lform;
}
// Indexes into constant method handles:
static final int
MH_cast = 0,
MH_selectAlternative = 1,
MH_copyAsPrimitiveArray = 2,
MH_fillNewTypedArray = 3,
MH_fillNewArray = 4,
MH_arrayIdentity = 5,
MH_countedLoopPred = 6,
MH_countedLoopStep = 7,
MH_initIterator = 8,
MH_iteratePred = 9,
MH_iterateNext = 10,
MH_Array_newInstance = 11,
MH_LIMIT = 12;
MH_cast = 0,
MH_selectAlternative = 1,
MH_countedLoopPred = 2,
MH_countedLoopStep = 3,
MH_initIterator = 4,
MH_iteratePred = 5,
MH_iterateNext = 6,
MH_Array_newInstance = 7,
MH_LIMIT = 8;
static MethodHandle getConstantHandle(int idx) {
MethodHandle handle = HANDLES[idx];
@ -2132,18 +1991,6 @@ abstract class MethodHandleImpl {
case MH_cast:
return IMPL_LOOKUP.findVirtual(Class.class, "cast",
MethodType.methodType(Object.class, Object.class));
case MH_copyAsPrimitiveArray:
return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "copyAsPrimitiveArray",
MethodType.methodType(Object.class, Wrapper.class, Object[].class));
case MH_arrayIdentity:
return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "identity",
MethodType.methodType(Object[].class, Object[].class));
case MH_fillNewArray:
return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "fillNewArray",
MethodType.methodType(Object[].class, Integer.class, Object[].class));
case MH_fillNewTypedArray:
return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "fillNewTypedArray",
MethodType.methodType(Object[].class, Object[].class, Integer.class, Object[].class));
case MH_selectAlternative:
return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "selectAlternative",
MethodType.methodType(MethodHandle.class, boolean.class, MethodHandle.class, MethodHandle.class));

@ -5731,14 +5731,7 @@ assertEquals("[top, [[up, down, strange], charm], bottom]",
MethodType newType = collectArgumentsChecks(target, pos, filter);
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());
LambdaForm lform = result.editor().collectArgumentsForm(1 + pos, collectorType.basicType());
return result.copyWithExtendL(newType, lform, filter);
}

@ -90,7 +90,8 @@ final class MethodTypeForm {
LF_VH_EX_INVOKER = 22, // VarHandle exact invoker
LF_VH_GEN_INVOKER = 23, // VarHandle generic invoker
LF_VH_GEN_LINKER = 24, // VarHandle generic linker
LF_LIMIT = 25;
LF_COLLECTOR = 25, // collector handle
LF_LIMIT = 26;
/** Return the type corresponding uniquely (1-1) to this MT-form.
* It might have any primitive returns or arguments, but will have no references except Object.

@ -113,7 +113,7 @@ public class MethodHandlesArityLimitsTest {
asList.asType(MethodType.genericMethodType(254));//does not throw IAE or WMTE
}
catch(WrongMethodTypeException wmte) {
Assert.fail("Unexpected WrongMethodTypeException thrown");
throw new AssertionError("Unexpected WrongMethodTypeException thrown", wmte);
}
asList.asType(MethodType.genericMethodType(255));//throws WMTE
}

@ -76,10 +76,13 @@ public class VarargsArrayTest {
}
}
private static class CustomClass {}
public static void testVarargsReferenceArray() throws Throwable {
testTypedVarargsArray(Object[].class);
testTypedVarargsArray(String[].class);
testTypedVarargsArray(Number[].class);
testTypedVarargsArray(CustomClass[].class);
}
public static void testVarargsPrimitiveArray() throws Throwable {
@ -171,6 +174,8 @@ public class VarargsArrayTest {
if (elem == null) {
if (elemType == String.class)
arg = "#"+arg;
if (elemType == CustomClass.class)
arg = new CustomClass();
arg = elemType.cast(arg); // just to make sure
} else {
switch (elem) {

@ -0,0 +1,91 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.java.lang.invoke;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.util.concurrent.TimeUnit;
@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@State(org.openjdk.jmh.annotations.Scope.Thread)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Fork(3)
public class TypedAsCollector {
static final MethodHandle MH_COLLECT_OBJECT = MethodHandles.identity(Object[].class).asCollector(Object[].class, 3);
static final MethodHandle MH_COLLECT_STRING = MethodHandles.identity(String[].class).asCollector(String[].class, 3);
static final MethodHandle MH_COLLECT_INT = MethodHandles.identity(int[].class).asCollector(int[].class, 3);
// uses a different code path to construct the collector
static final MethodHandle MH_COLLECT_OBJECT_HA = MethodHandles.identity(Object[].class).asCollector(Object[].class, 12);
static final MethodHandle MH_COLLECT_STRING_HA = MethodHandles.identity(String[].class).asCollector(String[].class, 12);
static final MethodHandle MH_COLLECT_INT_HA = MethodHandles.identity(int[].class).asCollector(int[].class, 12);
@Benchmark
public Object[] testObjectCollect() throws Throwable {
return (Object[]) MH_COLLECT_OBJECT.invokeExact((Object) "A", (Object) "B", (Object) "C");
}
@Benchmark
public Object[] testStringCollect() throws Throwable {
return (String[]) MH_COLLECT_STRING.invokeExact("A", "B", "C");
}
@Benchmark
public int[] testIntCollect() throws Throwable {
return (int[]) MH_COLLECT_INT.invokeExact(1, 2, 3);
}
@Benchmark
public Object[] testObjectCollectHighArity() throws Throwable {
return (Object[]) MH_COLLECT_OBJECT_HA.invokeExact(
(Object) "A", (Object) "B", (Object) "C", (Object) "D", (Object) "E", (Object) "F",
(Object) "G", (Object) "H", (Object) "I", (Object) "J", (Object) "K", (Object) "L");
}
@Benchmark
public Object[] testStringCollectHighArity() throws Throwable {
return (String[]) MH_COLLECT_STRING_HA.invokeExact(
"A", "B", "C", "D", "E", "F",
"G", "H", "I", "J", "K", "L");
}
@Benchmark
public int[] testIntCollectHighArity() throws Throwable {
return (int[]) MH_COLLECT_INT_HA.invokeExact(
1, 2, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12);
}
}