< 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 >