< prev index next >
src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java
Print this page
rev 54588 : imported patch constant_prepend
@@ -1577,26 +1577,39 @@
// Drop all remaining parameter types, leave only helper arguments:
MethodHandle mh;
mh = MethodHandles.dropArguments(NEW_STRING, 2, ptypes);
+ long initialLengthCoder = INITIAL_CODER;
+
// Mix in prependers. This happens when (byte[], long) = (storage, indexCoder) is already
// known from the combinators below. We are assembling the string backwards, so the index coded
// into indexCoder is the *ending* index.
+ String constant = null;
for (RecipeElement el : recipe.getElements()) {
// Do the prepend, and put "new" index at index 1
switch (el.getTag()) {
case TAG_CONST: {
- MethodHandle prepender = MethodHandles.insertArguments(prepender(String.class), 2, el.getValue());
- mh = MethodHandles.filterArgumentsWithCombiner(mh, 1, prepender,
- 1, 0 // indexCoder, storage
- );
+ String constantValue = el.getValue();
+ initialLengthCoder = (long)mixer(String.class).invoke(initialLengthCoder, constantValue);
+ if (constant != null) {
+ // fold sequential constants into one
+ constant = constant + constantValue;
+ } else {
+ constant = constantValue;
+ }
break;
}
case TAG_ARG: {
int pos = el.getArgPos();
- MethodHandle prepender = prepender(ptypes[pos]);
+ MethodHandle prepender;
+ if (constant != null) {
+ prepender = prepender(constant, ptypes[pos]);
+ constant = null;
+ } else {
+ prepender = prepender(ptypes[pos]);
+ }
mh = MethodHandles.filterArgumentsWithCombiner(mh, 1, prepender,
1, 0, // indexCoder, storage
2 + pos // selected argument
);
break;
@@ -1604,10 +1617,18 @@
default:
throw new StringConcatException("Unhandled tag: " + el.getTag());
}
}
+ // Insert any trailing constant
+ if (constant != null) {
+ MethodHandle prepender = MethodHandles.insertArguments(prepender(String.class), 2, constant);
+ mh = MethodHandles.filterArgumentsWithCombiner(mh, 1, prepender,
+ 1, 0 // indexCoder, storage
+ );
+ }
+
// Fold in byte[] instantiation at argument 0
mh = MethodHandles.foldArgumentsWithCombiner(mh, 0, NEW_ARRAY,
1 // index
);
@@ -1622,32 +1643,23 @@
// and deduce the coder from there. Arguments would be either converted to Strings
// during the initial filtering, or handled by specializations in MIXERS.
//
// The method handle shape before and after all mixers are combined in is:
// (long, <args>)String = ("indexCoder", <args>)
- long initialLengthCoder = INITIAL_CODER;
+
for (RecipeElement el : recipe.getElements()) {
- switch (el.getTag()) {
- case TAG_CONST:
- String constant = el.getValue();
- initialLengthCoder = (long)mixer(String.class).invoke(initialLengthCoder, constant);
- break;
- case TAG_ARG:
+ if (el.getTag() == TAG_ARG) {
int ac = el.getArgPos();
Class<?> argClass = ptypes[ac];
MethodHandle mix = mixer(argClass);
// Compute new "index" in-place using old value plus the appropriate argument.
mh = MethodHandles.filterArgumentsWithCombiner(mh, 0, mix,
0, // old-index
1 + ac // selected argument
);
-
- break;
- default:
- throw new StringConcatException("Unhandled tag: " + el.getTag());
}
}
// Insert initial length and coder value here.
// The method handle shape here is (<args>).
@@ -1663,10 +1675,16 @@
private static MethodHandle prepender(Class<?> cl) {
return PREPENDERS.computeIfAbsent(cl, PREPEND);
}
+ private static MethodHandle prepender(String constant, Class<?> cl) {
+ return MethodHandles.insertArguments(
+ CONSTANT_PREPENDERS.computeIfAbsent(cl, CONSTANT_PREPEND),
+ 2, constant);
+ }
+
private static MethodHandle mixer(Class<?> cl) {
return MIXERS.computeIfAbsent(cl, MIX);
}
// This one is deliberately non-lambdified to optimize startup time:
@@ -1677,10 +1695,19 @@
Wrapper.asPrimitiveType(c));
}
};
// This one is deliberately non-lambdified to optimize startup time:
+ private static final Function<Class<?>, MethodHandle> CONSTANT_PREPEND = new Function<Class<?>, MethodHandle>() {
+ @Override
+ public MethodHandle apply(Class<?> c) {
+ return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "prepend", long.class, long.class, byte[].class,
+ String.class, Wrapper.asPrimitiveType(c));
+ }
+ };
+
+ // This one is deliberately non-lambdified to optimize startup time:
private static final Function<Class<?>, MethodHandle> MIX = new Function<Class<?>, MethodHandle>() {
@Override
public MethodHandle apply(Class<?> c) {
return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mix", long.class, long.class,
Wrapper.asPrimitiveType(c));
@@ -1689,10 +1716,11 @@
private static final MethodHandle SIMPLE;
private static final MethodHandle NEW_STRING;
private static final MethodHandle NEW_ARRAY;
private static final ConcurrentMap<Class<?>, MethodHandle> PREPENDERS;
+ private static final ConcurrentMap<Class<?>, MethodHandle> CONSTANT_PREPENDERS;
private static final ConcurrentMap<Class<?>, MethodHandle> MIXERS;
private static final long INITIAL_CODER;
static {
try {
@@ -1700,10 +1728,11 @@
INITIAL_CODER = (long) initCoder.invoke();
} catch (Throwable e) {
throw new AssertionError(e);
}
+ CONSTANT_PREPENDERS = new ConcurrentHashMap<>();
PREPENDERS = new ConcurrentHashMap<>();
MIXERS = new ConcurrentHashMap<>();
SIMPLE = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "simpleConcat", String.class, Object.class, Object.class);
NEW_STRING = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newString", String.class, byte[].class, long.class);
< prev index next >