8212726: Replace some use of drop- and foldArguments with filtering argument combinator in StringConcatFactory
Reviewed-by: jlaskey, vlivanov
--- a/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java Tue Oct 23 10:55:59 2018 +0200
+++ b/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java Tue Oct 23 11:03:51 2018 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -85,7 +85,8 @@
PERMUTE_ARGS = 13,
LOCAL_TYPES = 14,
FOLD_SELECT_ARGS = 15,
- FOLD_SELECT_ARGS_TO_VOID = 16;
+ FOLD_SELECT_ARGS_TO_VOID = 16,
+ FILTER_SELECT_ARGS = 17;
private static final boolean STRESS_TEST = false; // turn on to disable most packing
private static final int
@@ -730,21 +731,23 @@
Name getCombiner = new Name(newData.getterFunction(oldData.fieldCount()), newBaseAddress);
Object[] combinerArgs = new Object[1 + combinerArity];
combinerArgs[0] = getCombiner;
- Name[] newParams;
+ Name newParam = null;
if (keepArguments) {
- newParams = new Name[0];
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 {
- newParams = new Name[combinerArity];
- for (int i = 0; i < newParams.length; i++) {
- newParams[i] = lambdaForm.parameter(1 + argPositions[i]);
+ 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]));
}
- System.arraycopy(newParams, 0,
- combinerArgs, 1, combinerArity);
}
Name callCombiner = new Name(combinerType, combinerArgs);
@@ -755,12 +758,13 @@
// insert new arguments, if needed
int argPos = pos + resultArity; // skip result parameter
- for (Name newParam : newParams) {
+ if (newParam != null) {
buf.insertParameter(argPos++, newParam);
+ exprPos++;
}
- assert(buf.lastIndexOf(callCombiner) == exprPos+1+newParams.length);
+ assert(buf.lastIndexOf(callCombiner) == exprPos+1);
if (!dropResult) {
- buf.replaceParameterByCopy(pos, exprPos+1+newParams.length);
+ buf.replaceParameterByCopy(pos, exprPos+1);
}
return buf.endEdit();
@@ -845,6 +849,20 @@
return putInCache(key, form);
}
+ LambdaForm filterArgumentsForm(int filterPos, MethodType combinerType, int ... argPositions) {
+ byte kind = Transform.FILTER_SELECT_ARGS;
+ int[] keyArgs = Arrays.copyOf(argPositions, argPositions.length + 1);
+ keyArgs[argPositions.length] = filterPos;
+ Transform key = Transform.of(kind, keyArgs);
+ LambdaForm form = getInCache(key);
+ if (form != null) {
+ assert(form.arity == lambdaForm.arity);
+ return form;
+ }
+ form = makeArgumentCombinationForm(filterPos, combinerType, argPositions, false, false);
+ return putInCache(key, form);
+ }
+
LambdaForm permuteArgumentsForm(int skip, int[] reorder) {
assert(skip == 1); // skip only the leading MH argument, names[0]
int length = lambdaForm.names.length;
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Tue Oct 23 10:55:59 2018 +0200
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Tue Oct 23 11:03:51 2018 +0200
@@ -4316,28 +4316,6 @@
return result;
}
- /**
- * As {@see foldArguments(MethodHandle, int, MethodHandle)}, but with the
- * added capability of selecting the arguments from the targets parameters
- * to call the combiner with. This allows us to avoid some simple cases of
- * permutations and padding the combiner with dropArguments to select the
- * right argument, which may ultimately produce fewer intermediaries.
- */
- static MethodHandle foldArguments(MethodHandle target, int pos, MethodHandle combiner, int ... argPositions) {
- MethodType targetType = target.type();
- MethodType combinerType = combiner.type();
- Class<?> rtype = foldArgumentChecks(pos, targetType, combinerType, argPositions);
- BoundMethodHandle result = target.rebind();
- boolean dropResult = rtype == void.class;
- LambdaForm lform = result.editor().foldArgumentsForm(1 + pos, dropResult, combinerType.basicType(), argPositions);
- MethodType newType = targetType;
- if (!dropResult) {
- newType = newType.dropParameterTypes(pos, pos + 1);
- }
- result = result.copyWithExtendL(newType, lform, combiner);
- return result;
- }
-
private static Class<?> foldArgumentChecks(int foldPos, MethodType targetType, MethodType combinerType) {
int foldArgs = combinerType.parameterCount();
Class<?> rtype = combinerType.returnType();
@@ -4359,15 +4337,78 @@
return rtype;
}
- private static Class<?> foldArgumentChecks(int foldPos, MethodType targetType, MethodType combinerType, int ... argPos) {
- int foldArgs = combinerType.parameterCount();
- if (argPos.length != foldArgs) {
+ /**
+ * Adapts a target method handle by pre-processing some of its arguments, then calling the target with the result
+ * of the pre-processing replacing the argument at the given position.
+ *
+ * @param target the method handle to invoke after arguments are combined
+ * @param position the position at which to start folding and at which to insert the folding result; if this is {@code
+ * 0}, the effect is the same as for {@link #foldArguments(MethodHandle, MethodHandle)}.
+ * @param combiner method handle to call initially on the incoming arguments
+ * @param argPositions indexes of the target to pick arguments sent to the combiner from
+ * @return method handle which incorporates the specified argument folding logic
+ * @throws NullPointerException if either argument is null
+ * @throws IllegalArgumentException if either of the following two conditions holds:
+ * (1) {@code combiner}'s return type is not the same as the argument type at position
+ * {@code pos} of the target signature;
+ * (2) the {@code N} argument types at positions {@code argPositions[1...N]} of the target signature are
+ * not identical with the argument types of {@code combiner}.
+ */
+ /*non-public*/ static MethodHandle filterArgumentsWithCombiner(MethodHandle target, int position, MethodHandle combiner, int ... argPositions) {
+ return argumentsWithCombiner(true, target, position, combiner, argPositions);
+ }
+
+ /**
+ * Adapts a target method handle by pre-processing some of its arguments, calling the target with the result of
+ * the pre-processing inserted into the original sequence of arguments at the given position.
+ *
+ * @param target the method handle to invoke after arguments are combined
+ * @param position the position at which to start folding and at which to insert the folding result; if this is {@code
+ * 0}, the effect is the same as for {@link #foldArguments(MethodHandle, MethodHandle)}.
+ * @param combiner method handle to call initially on the incoming arguments
+ * @param argPositions indexes of the target to pick arguments sent to the combiner from
+ * @return method handle which incorporates the specified argument folding logic
+ * @throws NullPointerException if either argument is null
+ * @throws IllegalArgumentException if either of the following two conditions holds:
+ * (1) {@code combiner}'s return type is non-{@code void} and not the same as the argument type at position
+ * {@code pos} of the target signature;
+ * (2) the {@code N} argument types at positions {@code argPositions[1...N]} of the target signature
+ * (skipping {@code position} where the {@code combiner}'s return will be folded in) are not identical
+ * with the argument types of {@code combiner}.
+ */
+ /*non-public*/ static MethodHandle foldArgumentsWithCombiner(MethodHandle target, int position, MethodHandle combiner, int ... argPositions) {
+ return argumentsWithCombiner(false, target, position, combiner, argPositions);
+ }
+
+ private static MethodHandle argumentsWithCombiner(boolean filter, MethodHandle target, int position, MethodHandle combiner, int ... argPositions) {
+ MethodType targetType = target.type();
+ MethodType combinerType = combiner.type();
+ Class<?> rtype = argumentsWithCombinerChecks(position, filter, targetType, combinerType, argPositions);
+ BoundMethodHandle result = target.rebind();
+
+ MethodType newType = targetType;
+ LambdaForm lform;
+ if (filter) {
+ lform = result.editor().filterArgumentsForm(1 + position, combinerType.basicType(), argPositions);
+ } else {
+ boolean dropResult = rtype == void.class;
+ lform = result.editor().foldArgumentsForm(1 + position, dropResult, combinerType.basicType(), argPositions);
+ if (!dropResult) {
+ newType = newType.dropParameterTypes(position, position + 1);
+ }
+ }
+ result = result.copyWithExtendL(newType, lform, combiner);
+ return result;
+ }
+
+ private static Class<?> argumentsWithCombinerChecks(int position, boolean filter, MethodType targetType, MethodType combinerType, int ... argPos) {
+ int combinerArgs = combinerType.parameterCount();
+ if (argPos.length != combinerArgs) {
throw newIllegalArgumentException("combiner and argument map must be equal size", combinerType, argPos.length);
}
Class<?> rtype = combinerType.returnType();
- int foldVals = rtype == void.class ? 0 : 1;
- boolean ok = true;
- for (int i = 0; i < foldArgs; i++) {
+
+ for (int i = 0; i < combinerArgs; i++) {
int arg = argPos[i];
if (arg < 0 || arg > targetType.parameterCount()) {
throw newIllegalArgumentException("arg outside of target parameterRange", targetType, arg);
@@ -4378,11 +4419,9 @@
+ " -> " + combinerType + ", map: " + Arrays.toString(argPos));
}
}
- if (ok && foldVals != 0 && combinerType.returnType() != targetType.parameterType(foldPos)) {
- ok = false;
+ if (filter && combinerType.returnType() != targetType.parameterType(position)) {
+ throw misMatchedTypes("target and combiner types", targetType, combinerType);
}
- if (!ok)
- throw misMatchedTypes("target and combiner types", targetType, combinerType);
return rtype;
}
--- a/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java Tue Oct 23 10:55:59 2018 +0200
+++ b/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java Tue Oct 23 11:03:51 2018 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -1533,21 +1533,20 @@
// *ending* index.
for (RecipeElement el : recipe.getElements()) {
// Do the prepend, and put "new" index at index 1
- mh = MethodHandles.dropArguments(mh, 2, int.class);
switch (el.getTag()) {
case TAG_CONST: {
MethodHandle prepender = MethodHandles.insertArguments(prepender(String.class), 3, el.getValue());
- mh = MethodHandles.foldArguments(mh, 1, prepender,
- 2, 0, 3 // index, storage, coder
+ mh = MethodHandles.filterArgumentsWithCombiner(mh, 1, prepender,
+ 1, 0, 2 // index, storage, coder
);
break;
}
case TAG_ARG: {
int pos = el.getArgPos();
MethodHandle prepender = prepender(ptypes[pos]);
- mh = MethodHandles.foldArguments(mh, 1, prepender,
- 2, 0, 3, // index, storage, coder
- 4 + pos // selected argument
+ mh = MethodHandles.filterArgumentsWithCombiner(mh, 1, prepender,
+ 1, 0, 2, // index, storage, coder
+ 3 + pos // selected argument
);
break;
}
@@ -1557,7 +1556,7 @@
}
// Fold in byte[] instantiation at argument 0
- mh = MethodHandles.foldArguments(mh, 0, NEW_ARRAY,
+ mh = MethodHandles.foldArgumentsWithCombiner(mh, 0, NEW_ARRAY,
1, 2 // index, coder
);
@@ -1572,7 +1571,7 @@
// and deduce the coder from there. Arguments would be either converted to Strings
// during the initial filtering, or handled by primitive specializations in CODER_MIXERS.
//
- // The method handle shape after all length and coder mixers is:
+ // The method handle shape before and after all length and coder mixers is:
// (int, byte, <args>)String = ("index", "coder", <args>)
byte initialCoder = INITIAL_CODER;
int initialLen = 0; // initial length, in characters
@@ -1589,44 +1588,27 @@
Class<?> argClass = ptypes[ac];
MethodHandle lm = lengthMixer(argClass);
- // Read these bottom up:
-
if (argClass.isPrimitive() && argClass != char.class) {
-
- // 3. Drop old index, producing ("new-index", "coder", <args>)
- mh = MethodHandles.dropArguments(mh, 1, int.class);
-
- // 2. Compute "new-index", producing ("new-index", "old-index", "coder", <args>)
- // Length mixer needs old index, plus the appropriate argument
- mh = MethodHandles.foldArguments(mh, 0, lm,
- 1, // old-index
- 3 + ac // selected argument
+ // Compute new "index" in-place using old value plus the appropriate argument.
+ mh = MethodHandles.filterArgumentsWithCombiner(mh, 0, lm,
+ 0, // old-index
+ 2 + ac // selected argument
);
- // 1. The mh shape here is ("old-index", "coder", <args>); we don't need to recalculate
- // the coder for non-char primitive arguments
-
} else {
MethodHandle cm = coderMixer(argClass);
- // 4. Drop old index and coder, producing ("new-index", "new-coder", <args>)
- mh = MethodHandles.dropArguments(mh, 2, int.class, byte.class);
-
- // 3. Compute "new-index", producing ("new-index", "new-coder", "old-index", "old-coder", <args>)
- // Length mixer needs old index, plus the appropriate argument
- mh = MethodHandles.foldArguments(mh, 0, lm,
- 2, // old-index
- 4 + ac // selected argument
+ // Compute new "index" in-place using old value plus the appropriate argument.
+ mh = MethodHandles.filterArgumentsWithCombiner(mh, 0, lm,
+ 0, // old-index
+ 2 + ac // selected argument
);
- // 2. Compute "new-coder", producing ("new-coder", "old-index", "old-coder", <args>)
- // Coder mixer needs old coder, plus the appropriate argument.
- mh = MethodHandles.foldArguments(mh, 0, cm,
- 2, // old-coder
- 3 + ac // selected argument
+ // Compute new "coder" in-place using old value plus the appropriate argument.
+ mh = MethodHandles.filterArgumentsWithCombiner(mh, 1, cm,
+ 1, // old-coder
+ 2 + ac // selected argument
);
-
- // 1. The mh shape here is ("old-index", "old-coder", <args>)
}
break;