src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java
changeset 52326 77018c2b97df
parent 52226 b4b932c6001f
child 52497 34510f65fb58
--- a/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java	Tue Oct 30 09:06:08 2018 +0100
+++ b/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java	Tue Oct 30 09:34:50 2018 +0100
@@ -318,6 +318,13 @@
         }
 
         @Override
+        public String toString() {
+            return "Recipe{" +
+                    "elements=" + elements +
+                    '}';
+        }
+
+        @Override
         public int hashCode() {
             return elements.hashCode();
         }
@@ -368,6 +375,15 @@
         }
 
         @Override
+        public String toString() {
+            return "RecipeElement{" +
+                    "value='" + value + '\'' +
+                    ", argPos=" + argPos +
+                    ", tag=" + tag +
+                    '}';
+        }
+
+        @Override
         public int hashCode() {
             return (int)tag;
         }
@@ -1520,24 +1536,24 @@
             }
 
             // Start building the combinator tree. The tree "starts" with (<parameters>)String, and "finishes"
-            // with the (int, byte[], byte)String in String helper. The combinators are assembled bottom-up,
-            // which makes the code arguably hard to read.
+            // with the (byte[], long)String shape to invoke newString in StringConcatHelper. The combinators are
+            // assembled bottom-up, which makes the code arguably hard to read.
 
             // Drop all remaining parameter types, leave only helper arguments:
             MethodHandle mh;
 
-            mh = MethodHandles.dropArguments(NEW_STRING, 3, ptypes);
+            mh = MethodHandles.dropArguments(NEW_STRING, 2, ptypes);
 
-            // Mix in prependers. This happens when (byte[], int, byte) = (storage, index, coder) is already
-            // known from the combinators below. We are assembling the string backwards, so "index" is the
-            // *ending* index.
+            // 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.
             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), 3, el.getValue());
+                        MethodHandle prepender = MethodHandles.insertArguments(prepender(String.class), 2, el.getValue());
                         mh = MethodHandles.filterArgumentsWithCombiner(mh, 1, prepender,
-                                1, 0, 2 // index, storage, coder
+                                1, 0 // indexCoder, storage
                         );
                         break;
                     }
@@ -1545,8 +1561,8 @@
                         int pos = el.getArgPos();
                         MethodHandle prepender = prepender(ptypes[pos]);
                         mh = MethodHandles.filterArgumentsWithCombiner(mh, 1, prepender,
-                                1, 0, 2, // index, storage, coder
-                                3 + pos  // selected argument
+                                1, 0, // indexCoder, storage
+                                2 + pos  // selected argument
                         );
                         break;
                     }
@@ -1557,7 +1573,7 @@
 
             // Fold in byte[] instantiation at argument 0
             mh = MethodHandles.foldArgumentsWithCombiner(mh, 0, NEW_ARRAY,
-                    1, 2 // index, coder
+                    1 // index
             );
 
             // Start combining length and coder mixers.
@@ -1569,47 +1585,28 @@
             // Coders are more interesting. Only Object, String and char arguments (and constants)
             // can have non-Latin1 encoding. It is easier to blindly convert constants to String,
             // 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.
+            // during the initial filtering, or handled by specializations in MIXERS.
             //
-            // 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
+            // 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();
-                        initialCoder = (byte) coderMixer(String.class).invoke(initialCoder, constant);
-                        initialLen += constant.length();
+                        initialLengthCoder = (long)mixer(String.class).invoke(initialLengthCoder, constant);
                         break;
                     case TAG_ARG:
                         int ac = el.getArgPos();
 
                         Class<?> argClass = ptypes[ac];
-                        MethodHandle lm = lengthMixer(argClass);
-
-                        if (argClass.isPrimitive() && argClass != char.class) {
-                            // 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
-                            );
-
-                        } else {
-                            MethodHandle cm = coderMixer(argClass);
+                        MethodHandle mix = mixer(argClass);
 
-                            // 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
-                            );
-
-                            // 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
-                            );
-                        }
+                        // 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:
@@ -1617,9 +1614,9 @@
                 }
             }
 
-            // Insert initial lengths and coders here.
+            // Insert initial length and coder value here.
             // The method handle shape here is (<args>).
-            mh = MethodHandles.insertArguments(mh, 0, initialLen, initialCoder);
+            mh = MethodHandles.insertArguments(mh, 0, initialLengthCoder);
 
             // Apply filters, converting the arguments:
             if (filters != null) {
@@ -1630,45 +1627,34 @@
         }
 
         @ForceInline
-        private static byte[] newArray(int length, byte coder) {
-            return (byte[]) UNSAFE.allocateUninitializedArray(byte.class, length << coder);
+        private static byte[] newArray(long indexCoder) {
+            byte coder = (byte)(indexCoder >> 32);
+            int index = ((int)indexCoder & 0x7FFFFFFF);
+            return (byte[]) UNSAFE.allocateUninitializedArray(byte.class, index << coder);
         }
 
         private static MethodHandle prepender(Class<?> cl) {
             return PREPENDERS.computeIfAbsent(cl, PREPEND);
         }
 
-        private static MethodHandle coderMixer(Class<?> cl) {
-            return CODER_MIXERS.computeIfAbsent(cl, CODER_MIX);
-        }
-
-        private static MethodHandle lengthMixer(Class<?> cl) {
-            return LENGTH_MIXERS.computeIfAbsent(cl, LENGTH_MIX);
+        private static MethodHandle mixer(Class<?> cl) {
+            return MIXERS.computeIfAbsent(cl, MIX);
         }
 
         // This one is deliberately non-lambdified to optimize startup time:
         private static final Function<Class<?>, MethodHandle> PREPEND = new Function<Class<?>, MethodHandle>() {
             @Override
             public MethodHandle apply(Class<?> c) {
-                return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "prepend", int.class, int.class, byte[].class, byte.class,
+                return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "prepend", long.class, long.class, byte[].class,
                         Wrapper.asPrimitiveType(c));
             }
         };
 
         // This one is deliberately non-lambdified to optimize startup time:
-        private static final Function<Class<?>, MethodHandle> CODER_MIX = new Function<Class<?>, MethodHandle>() {
+        private static final Function<Class<?>, MethodHandle> MIX = new Function<Class<?>, MethodHandle>() {
             @Override
             public MethodHandle apply(Class<?> c) {
-                return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mixCoder", byte.class, byte.class,
-                        Wrapper.asPrimitiveType(c));
-            }
-        };
-
-        // This one is deliberately non-lambdified to optimize startup time:
-        private static final Function<Class<?>, MethodHandle> LENGTH_MIX = new Function<Class<?>, MethodHandle>() {
-            @Override
-            public MethodHandle apply(Class<?> c) {
-                return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mixLen", int.class, int.class,
+                return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mix", long.class, long.class,
                         Wrapper.asPrimitiveType(c));
             }
         };
@@ -1676,26 +1662,24 @@
         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> LENGTH_MIXERS;
-        private static final ConcurrentMap<Class<?>, MethodHandle> CODER_MIXERS;
-        private static final byte INITIAL_CODER;
+        private static final ConcurrentMap<Class<?>, MethodHandle> MIXERS;
+        private static final long INITIAL_CODER;
         static final Class<?> STRING_HELPER;
 
         static {
             try {
                 STRING_HELPER = Class.forName("java.lang.StringConcatHelper");
-                MethodHandle initCoder = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "initialCoder", byte.class);
-                INITIAL_CODER = (byte) initCoder.invoke();
+                MethodHandle initCoder = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "initialCoder", long.class);
+                INITIAL_CODER = (long) initCoder.invoke();
             } catch (Throwable e) {
                 throw new AssertionError(e);
             }
 
             PREPENDERS = new ConcurrentHashMap<>();
-            LENGTH_MIXERS = new ConcurrentHashMap<>();
-            CODER_MIXERS = new ConcurrentHashMap<>();
+            MIXERS = new ConcurrentHashMap<>();
 
-            NEW_STRING = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newString", String.class, byte[].class, int.class, byte.class);
-            NEW_ARRAY  = lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleInlineCopyStrategy.class, "newArray", byte[].class, int.class, byte.class);
+            NEW_STRING = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newString", String.class, byte[].class, long.class);
+            NEW_ARRAY  = lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleInlineCopyStrategy.class, "newArray", byte[].class, long.class);
         }
     }