< prev index next >

src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java

Print this page




  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.lang.invoke;
  27 
  28 import jdk.internal.org.objectweb.asm.ClassWriter;
  29 import jdk.internal.org.objectweb.asm.Label;
  30 import jdk.internal.org.objectweb.asm.MethodVisitor;
  31 import jdk.internal.org.objectweb.asm.Opcodes;
  32 import jdk.internal.vm.annotation.ForceInline;
  33 import sun.misc.Unsafe;
  34 
  35 import java.lang.invoke.MethodHandles.Lookup;
  36 import java.security.AccessController;
  37 import java.security.PrivilegedAction;
  38 import java.util.*;
  39 import java.util.concurrent.ConcurrentHashMap;
  40 import java.util.concurrent.ConcurrentMap;
  41 import java.util.function.Function;
  42 
  43 import static jdk.internal.org.objectweb.asm.Opcodes.*;
  44 
  45 /**
  46  * <p>Methods to facilitate the creation of String concatenation methods, that
  47  * can be used to efficiently concatenate a known number of arguments of known
  48  * types, possibly after type adaptation and partial evaluation of arguments.
  49  * These methods are typically used as <em>bootstrap methods</em> for {@code
  50  * invokedynamic} call sites, to support the <em>string concatenation</em>
  51  * feature of the Java Programming Language.
  52  *
  53  * <p>Indirect access to the behavior specified by the provided {@code


1480                 mh = MethodHandles.permuteArguments(mh, nmt, swap10(nmt.parameterCount()));
1481             }
1482 
1483             // Fold in byte[] instantiation at argument 0.
1484             MethodHandle combiner = MethodHandles.dropArguments(NEW_ARRAY, 2, ptypesList);
1485             mh = MethodHandles.foldArguments(mh, combiner);
1486 
1487             // Start combining length and coder mixers.
1488             //
1489             // Length is easy: constant lengths can be computed on the spot, and all non-constant
1490             // shapes have been either converted to Strings, or explicit methods for getting the
1491             // string length out of primitives are provided.
1492             //
1493             // Coders are more interesting. Only Object, String and char arguments (and constants)
1494             // can have non-Latin1 encoding. It is easier to blindly convert constants to String,
1495             // and deduce the coder from there. Arguments would be either converted to Strings
1496             // during the initial filtering, or handled by primitive specializations in CODER_MIXERS.
1497             //
1498             // The method handle shape after all length and coder mixers is:
1499             //   (int, byte, <args>)String = ("index", "coder", <args>)
1500             byte initialCoder = 0; // initial coder
1501             int initialLen = 0;    // initial length, in characters
1502             for (RecipeElement el : recipe.getElements()) {
1503                 switch (el.getTag()) {
1504                     case CONST: {
1505                         Object constant = el.getValue();
1506                         String s = constant.toString();
1507                         initialCoder = (byte) coderMixer(String.class).invoke(initialCoder, s);
1508                         initialLen += s.length();
1509                         break;
1510                     }
1511                     case ARG: {
1512                         int ac = el.getArgPos();
1513 
1514                         Class<?> argClass = ptypesList.get(ac);
1515                         MethodHandle lm = selectArgument(lengthMixer(argClass), 1, ptypesList, ac);
1516                         lm = MethodHandles.dropArguments(lm, 0, byte.class); // (*)
1517                         lm = MethodHandles.dropArguments(lm, 2, byte.class);
1518 
1519                         MethodHandle cm = selectArgument(coderMixer(argClass),  1, ptypesList, ac);
1520                         cm = MethodHandles.dropArguments(cm, 0, int.class);  // (**)


1613             public MethodHandle apply(Class<?> c) {
1614                 return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mixCoder", byte.class, byte.class, c);
1615             }
1616         };
1617 
1618         // This one is deliberately non-lambdified to optimize startup time:
1619         private static final Function<Class<?>, MethodHandle> LENGTH_MIX = new Function<Class<?>, MethodHandle>() {
1620             @Override
1621             public MethodHandle apply(Class<?> c) {
1622                 return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mixLen", int.class, int.class, c);
1623             }
1624         };
1625 
1626         private static final MethodHandle NEW_STRING;
1627         private static final MethodHandle CHECK_INDEX;
1628         private static final MethodHandle NEW_ARRAY;
1629         private static final ConcurrentMap<Class<?>, MethodHandle> PREPENDERS;
1630         private static final ConcurrentMap<Class<?>, MethodHandle> LENGTH_MIXERS;
1631         private static final ConcurrentMap<Class<?>, MethodHandle> CODER_MIXERS;
1632         private static final Class<?> STRING_HELPER;

1633 
1634         static {
1635             try {
1636                 STRING_HELPER = Class.forName("java.lang.StringConcatHelper");
1637             } catch (ClassNotFoundException e) {


1638                 throw new AssertionError(e);
1639             }
1640 
1641             PREPENDERS = new ConcurrentHashMap<>();
1642             LENGTH_MIXERS = new ConcurrentHashMap<>();
1643             CODER_MIXERS = new ConcurrentHashMap<>();
1644 
1645             NEW_STRING = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newString", String.class, byte[].class, byte.class);
1646             NEW_ARRAY  = lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleInlineCopyStrategy.class, "newArray", byte[].class, int.class, byte.class);
1647 
1648             if (DEBUG) {
1649                 CHECK_INDEX = lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleInlineCopyStrategy.class, "checkIndex", int.class, int.class);
1650             } else {
1651                 CHECK_INDEX = null;
1652             }
1653         }
1654     }
1655 
1656     /**
1657      * Public gateways to public "stringify" methods. These methods have the form String apply(T obj), and normally




  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.lang.invoke;
  27 
  28 import jdk.internal.org.objectweb.asm.ClassWriter;
  29 import jdk.internal.org.objectweb.asm.Label;
  30 import jdk.internal.org.objectweb.asm.MethodVisitor;
  31 import jdk.internal.org.objectweb.asm.Opcodes;
  32 import jdk.internal.vm.annotation.ForceInline;
  33 import jdk.internal.misc.Unsafe;
  34 
  35 import java.lang.invoke.MethodHandles.Lookup;
  36 import java.security.AccessController;
  37 import java.security.PrivilegedAction;
  38 import java.util.*;
  39 import java.util.concurrent.ConcurrentHashMap;
  40 import java.util.concurrent.ConcurrentMap;
  41 import java.util.function.Function;
  42 
  43 import static jdk.internal.org.objectweb.asm.Opcodes.*;
  44 
  45 /**
  46  * <p>Methods to facilitate the creation of String concatenation methods, that
  47  * can be used to efficiently concatenate a known number of arguments of known
  48  * types, possibly after type adaptation and partial evaluation of arguments.
  49  * These methods are typically used as <em>bootstrap methods</em> for {@code
  50  * invokedynamic} call sites, to support the <em>string concatenation</em>
  51  * feature of the Java Programming Language.
  52  *
  53  * <p>Indirect access to the behavior specified by the provided {@code


1480                 mh = MethodHandles.permuteArguments(mh, nmt, swap10(nmt.parameterCount()));
1481             }
1482 
1483             // Fold in byte[] instantiation at argument 0.
1484             MethodHandle combiner = MethodHandles.dropArguments(NEW_ARRAY, 2, ptypesList);
1485             mh = MethodHandles.foldArguments(mh, combiner);
1486 
1487             // Start combining length and coder mixers.
1488             //
1489             // Length is easy: constant lengths can be computed on the spot, and all non-constant
1490             // shapes have been either converted to Strings, or explicit methods for getting the
1491             // string length out of primitives are provided.
1492             //
1493             // Coders are more interesting. Only Object, String and char arguments (and constants)
1494             // can have non-Latin1 encoding. It is easier to blindly convert constants to String,
1495             // and deduce the coder from there. Arguments would be either converted to Strings
1496             // during the initial filtering, or handled by primitive specializations in CODER_MIXERS.
1497             //
1498             // The method handle shape after all length and coder mixers is:
1499             //   (int, byte, <args>)String = ("index", "coder", <args>)
1500             byte initialCoder = INITIAL_CODER;
1501             int initialLen = 0;    // initial length, in characters
1502             for (RecipeElement el : recipe.getElements()) {
1503                 switch (el.getTag()) {
1504                     case CONST: {
1505                         Object constant = el.getValue();
1506                         String s = constant.toString();
1507                         initialCoder = (byte) coderMixer(String.class).invoke(initialCoder, s);
1508                         initialLen += s.length();
1509                         break;
1510                     }
1511                     case ARG: {
1512                         int ac = el.getArgPos();
1513 
1514                         Class<?> argClass = ptypesList.get(ac);
1515                         MethodHandle lm = selectArgument(lengthMixer(argClass), 1, ptypesList, ac);
1516                         lm = MethodHandles.dropArguments(lm, 0, byte.class); // (*)
1517                         lm = MethodHandles.dropArguments(lm, 2, byte.class);
1518 
1519                         MethodHandle cm = selectArgument(coderMixer(argClass),  1, ptypesList, ac);
1520                         cm = MethodHandles.dropArguments(cm, 0, int.class);  // (**)


1613             public MethodHandle apply(Class<?> c) {
1614                 return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mixCoder", byte.class, byte.class, c);
1615             }
1616         };
1617 
1618         // This one is deliberately non-lambdified to optimize startup time:
1619         private static final Function<Class<?>, MethodHandle> LENGTH_MIX = new Function<Class<?>, MethodHandle>() {
1620             @Override
1621             public MethodHandle apply(Class<?> c) {
1622                 return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mixLen", int.class, int.class, c);
1623             }
1624         };
1625 
1626         private static final MethodHandle NEW_STRING;
1627         private static final MethodHandle CHECK_INDEX;
1628         private static final MethodHandle NEW_ARRAY;
1629         private static final ConcurrentMap<Class<?>, MethodHandle> PREPENDERS;
1630         private static final ConcurrentMap<Class<?>, MethodHandle> LENGTH_MIXERS;
1631         private static final ConcurrentMap<Class<?>, MethodHandle> CODER_MIXERS;
1632         private static final Class<?> STRING_HELPER;
1633         private static final byte INITIAL_CODER;
1634 
1635         static {
1636             try {
1637                 STRING_HELPER = Class.forName("java.lang.StringConcatHelper");
1638                 MethodHandle initCoder = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "initialCoder", byte.class);
1639                 INITIAL_CODER = (byte) initCoder.invoke();
1640             } catch (Throwable e) {
1641                 throw new AssertionError(e);
1642             }
1643 
1644             PREPENDERS = new ConcurrentHashMap<>();
1645             LENGTH_MIXERS = new ConcurrentHashMap<>();
1646             CODER_MIXERS = new ConcurrentHashMap<>();
1647 
1648             NEW_STRING = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newString", String.class, byte[].class, byte.class);
1649             NEW_ARRAY  = lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleInlineCopyStrategy.class, "newArray", byte[].class, int.class, byte.class);
1650 
1651             if (DEBUG) {
1652                 CHECK_INDEX = lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleInlineCopyStrategy.class, "checkIndex", int.class, int.class);
1653             } else {
1654                 CHECK_INDEX = null;
1655             }
1656         }
1657     }
1658 
1659     /**
1660      * Public gateways to public "stringify" methods. These methods have the form String apply(T obj), and normally


< prev index next >