# HG changeset patch # User redestad # Date 1540205826 -7200 # Mon Oct 22 12:57:06 2018 +0200 # Node ID 0bac05d09cd7d818cdb7d1936e709453bc12503c # Parent 5e894b0f5e635feeee02298afeea19ffd58bb6d9 8212726: Replace some use of drop- and foldArguments with filtering argument combinator in StringConcatFactory Reviewed-by: TBD diff --git a/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java b/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java --- a/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java +++ b/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java @@ -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; diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java --- a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java @@ -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; } diff --git a/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java b/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java --- a/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java +++ b/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java @@ -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, )String = ("index", "coder", ) 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", ) - mh = MethodHandles.dropArguments(mh, 1, int.class); - - // 2. Compute "new-index", producing ("new-index", "old-index", "coder", ) - // 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", ); 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", ) - mh = MethodHandles.dropArguments(mh, 2, int.class, byte.class); - - // 3. Compute "new-index", producing ("new-index", "new-coder", "old-index", "old-coder", ) - // 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", ) - // 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", ) } break;