< prev index next >

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

Print this page
rev 58565 : 8238358: Implementation of JEP 371: Hidden Classes
Reviewed-by: duke
Contributed-by: mandy.chung@oracle.com, lois.foltan@oracle.com, david.holmes@oracle.com, harold.seigel@oracle.com, serguei.spitsyn@oracle.com, alex.buckley@oracle.com, jamsheed.c.m@oracle.com
rev 58567 : [mq]: rename-isHidden


   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  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.misc.Unsafe;

  29 import jdk.internal.misc.VM;
  30 import jdk.internal.org.objectweb.asm.ClassWriter;
  31 import jdk.internal.org.objectweb.asm.Label;
  32 import jdk.internal.org.objectweb.asm.MethodVisitor;
  33 import jdk.internal.org.objectweb.asm.Opcodes;
  34 import sun.invoke.util.Wrapper;
  35 
  36 import java.lang.invoke.MethodHandles.Lookup;
  37 import java.util.ArrayList;
  38 import java.util.Arrays;
  39 import java.util.List;
  40 import java.util.Objects;
  41 import java.util.concurrent.ConcurrentHashMap;
  42 import java.util.concurrent.ConcurrentMap;
  43 import java.util.function.Function;
  44 



  45 import static jdk.internal.org.objectweb.asm.Opcodes.*;
  46 
  47 /**
  48  * <p>Methods to facilitate the creation of String concatenation methods, that
  49  * can be used to efficiently concatenate a known number of arguments of known
  50  * types, possibly after type adaptation and partial evaluation of arguments.
  51  * These methods are typically used as <em>bootstrap methods</em> for {@code
  52  * invokedynamic} call sites, to support the <em>string concatenation</em>
  53  * feature of the Java Programming Language.
  54  *
  55  * <p>Indirect access to the behavior specified by the provided {@code
  56  * MethodHandle} proceeds in order through two phases:
  57  *
  58  * <ol>
  59  *     <li><em>Linkage</em> occurs when the methods in this class are invoked.
  60  * They take as arguments a method type describing the concatenated arguments
  61  * count and types, and optionally the String <em>recipe</em>, plus the
  62  * constants that participate in the String concatenation. The details on
  63  * accepted recipe shapes are described further below. Linkage may involve
  64  * dynamically loading a new class that implements the expected concatenation


 116     /**
 117      * Maximum number of argument slots in String Concat call.
 118      *
 119      * While the maximum number of argument slots that indy call can handle is 253,
 120      * we do not use all those slots, to let the strategies with MethodHandle
 121      * combinators to use some arguments.
 122      */
 123     private static final int MAX_INDY_CONCAT_ARG_SLOTS = 200;
 124 
 125     /**
 126      * Concatenation strategy to use. See {@link Strategy} for possible options.
 127      * This option is controllable with -Djava.lang.invoke.stringConcat JDK option.
 128      */
 129     private static Strategy STRATEGY;
 130 
 131     /**
 132      * Default strategy to use for concatenation.
 133      */
 134     private static final Strategy DEFAULT_STRATEGY = Strategy.MH_INLINE_SIZED_EXACT;
 135 


 136     private enum Strategy {
 137         /**
 138          * Bytecode generator, calling into {@link java.lang.StringBuilder}.
 139          */
 140         BC_SB,
 141 
 142         /**
 143          * Bytecode generator, calling into {@link java.lang.StringBuilder};
 144          * but trying to estimate the required storage.
 145          */
 146         BC_SB_SIZED,
 147 
 148         /**
 149          * Bytecode generator, calling into {@link java.lang.StringBuilder};
 150          * but computing the required storage exactly.
 151          */
 152         BC_SB_SIZED_EXACT,
 153 
 154         /**
 155          * MethodHandle-based generator, that in the end calls into {@link java.lang.StringBuilder}.


 172 
 173     /**
 174      * Enables debugging: this may print debugging messages, perform additional (non-neutral for performance)
 175      * checks, etc.
 176      */
 177     private static final boolean DEBUG;
 178 
 179     /**
 180      * Enables caching of strategy stubs. This may improve the linkage time by reusing the generated
 181      * code, at the expense of contaminating the profiles.
 182      */
 183     private static final boolean CACHE_ENABLE;
 184 
 185     private static final ConcurrentMap<Key, MethodHandle> CACHE;
 186 
 187     /**
 188      * Dump generated classes to disk, for debugging purposes.
 189      */
 190     private static final ProxyClassesDumper DUMPER;
 191 
 192     private static final Class<?> STRING_HELPER;
 193 
 194     static {
 195         // In case we need to double-back onto the StringConcatFactory during this
 196         // static initialization, make sure we have the reasonable defaults to complete
 197         // the static initialization properly. After that, actual users would use
 198         // the proper values we have read from the properties.
 199         STRATEGY = DEFAULT_STRATEGY;
 200         // CACHE_ENABLE = false; // implied
 201         // CACHE = null;         // implied
 202         // DEBUG = false;        // implied
 203         // DUMPER = null;        // implied
 204 
 205         try {
 206             STRING_HELPER = Class.forName("java.lang.StringConcatHelper");
 207         } catch (Throwable e) {
 208             throw new AssertionError(e);
 209         }
 210 
 211         final String strategy =
 212                 VM.getSavedProperty("java.lang.invoke.stringConcat");
 213         CACHE_ENABLE = Boolean.parseBoolean(
 214                 VM.getSavedProperty("java.lang.invoke.stringConcat.cache"));
 215         DEBUG = Boolean.parseBoolean(
 216                 VM.getSavedProperty("java.lang.invoke.stringConcat.debug"));
 217         final String dumpPath =
 218                 VM.getSavedProperty("java.lang.invoke.stringConcat.dumpClasses");
 219 
 220         STRATEGY = (strategy == null) ? DEFAULT_STRATEGY : Strategy.valueOf(strategy);
 221         CACHE = CACHE_ENABLE ? new ConcurrentHashMap<>() : null;
 222         DUMPER = (dumpPath == null) ? null : ProxyClassesDumper.getInstance(dumpPath);
 223     }
 224 
 225     /**
 226      * Cache key is a composite of:
 227      *   - class name, that lets to disambiguate stubs, to avoid excess sharing
 228      *   - method type, describing the dynamic arguments for concatenation
 229      *   - concat recipe, describing the constants and concat shape
 230      */


 701         Class<?>[] ptypes = null;
 702         for (int i = 0; i < args.parameterCount(); i++) {
 703             Class<?> ptype = args.parameterType(i);
 704             if (!ptype.isPrimitive() &&
 705                     ptype != String.class &&
 706                     ptype != Object.class) { // truncate to Object
 707                 if (ptypes == null) {
 708                     ptypes = args.parameterArray();
 709                 }
 710                 ptypes[i] = Object.class;
 711             }
 712             // else other primitives or String or Object (unchanged)
 713         }
 714         return (ptypes != null)
 715                 ? MethodType.methodType(args.returnType(), ptypes)
 716                 : args;
 717     }
 718 
 719     private static String getClassName(Class<?> hostClass) throws StringConcatException {
 720         /*
 721           When cache is enabled, we want to cache as much as we can.
 722 
 723           However, there are two peculiarities:
 724 
 725            a) The generated class should stay within the same package as the
 726               host class, to allow Unsafe.defineAnonymousClass access controls
 727               to work properly. JDK may choose to fail with IllegalAccessException
 728               when accessing a VM anonymous class with non-privileged callers,
 729               see JDK-8058575.
 730 
 731            b) If we mark the stub with some prefix, say, derived from the package
 732               name because of (a), we can technically use that stub in other packages.
 733               But the call stack traces would be extremely puzzling to unsuspecting users
 734               and profiling tools: whatever stub wins the race, would be linked in all
 735               similar callsites.
 736 
 737            Therefore, we set the class prefix to match the host class package, and use
 738            the prefix as the cache key too. This only affects BC_* strategies, and only when
 739            cache is enabled.
 740          */
 741 
 742         switch (STRATEGY) {
 743             case BC_SB:
 744             case BC_SB_SIZED:
 745             case BC_SB_SIZED_EXACT: {
 746                 if (CACHE_ENABLE) {
 747                     String pkgName = hostClass.getPackageName();
 748                     return (pkgName != null && !pkgName.isEmpty() ? pkgName.replace('.', '/') + "/" : "") + "Stubs$$StringConcat";
 749                 } else {
 750                     return hostClass.getName().replace('.', '/') + "$$StringConcat";


 751                 }
 752             }
 753             case MH_SB_SIZED:
 754             case MH_SB_SIZED_EXACT:
 755             case MH_INLINE_SIZED_EXACT:
 756                 // MethodHandle strategies do not need a class name.
 757                 return "";
 758             default:
 759                 throw new StringConcatException("Concatenation strategy " + STRATEGY + " is not implemented");
 760         }
 761     }
 762 
 763     private static MethodHandle generate(Lookup lookup, String className, MethodType mt, Recipe recipe) throws StringConcatException {
 764         try {
 765             switch (STRATEGY) {
 766                 case BC_SB:
 767                     return BytecodeStringBuilderStrategy.generate(lookup, className, mt, recipe, Mode.DEFAULT);
 768                 case BC_SB_SIZED:
 769                     return BytecodeStringBuilderStrategy.generate(lookup, className, mt, recipe, Mode.SIZED);
 770                 case BC_SB_SIZED_EXACT:


 802         boolean isSized() {
 803             return sized;
 804         }
 805 
 806         boolean isExact() {
 807             return exact;
 808         }
 809     }
 810 
 811     /**
 812      * Bytecode StringBuilder strategy.
 813      *
 814      * <p>This strategy operates in three modes, gated by {@link Mode}.
 815      *
 816      * <p><b>{@link Strategy#BC_SB}: "bytecode StringBuilder".</b>
 817      *
 818      * <p>This strategy spins up the bytecode that has the same StringBuilder
 819      * chain javac would otherwise emit. This strategy uses only the public API,
 820      * and comes as the baseline for the current JDK behavior. On other words,
 821      * this strategy moves the javac generated bytecode to runtime. The
 822      * generated bytecode is loaded via Unsafe.defineAnonymousClass, but with
 823      * the caller class coming from the BSM -- in other words, the protection
 824      * guarantees are inherited from the method where invokedynamic was
 825      * originally called. This means, among other things, that the bytecode is
 826      * verified for all non-JDK uses.
 827      *
 828      * <p><b>{@link Strategy#BC_SB_SIZED}: "bytecode StringBuilder, but
 829      * sized".</b>
 830      *
 831      * <p>This strategy acts similarly to {@link Strategy#BC_SB}, but it also
 832      * tries to guess the capacity required for StringBuilder to accept all
 833      * arguments without resizing. This strategy only makes an educated guess:
 834      * it only guesses the space required for known types (e.g. primitives and
 835      * Strings), but does not otherwise convert arguments. Therefore, the
 836      * capacity estimate may be wrong, and StringBuilder may have to
 837      * transparently resize or trim when doing the actual concatenation. While
 838      * this does not constitute a correctness issue (in the end, that what BC_SB
 839      * has to do anyway), this does pose a potential performance problem.
 840      *
 841      * <p><b>{@link Strategy#BC_SB_SIZED_EXACT}: "bytecode StringBuilder, but
 842      * sized exactly".</b>
 843      *
 844      * <p>This strategy improves on @link Strategy#BC_SB_SIZED}, by first
 845      * converting all arguments to String in order to get the exact capacity
 846      * StringBuilder should have. The conversion is done via the public
 847      * String.valueOf and/or Object.toString methods, and does not touch any
 848      * private String API.
 849      */
 850     private static final class BytecodeStringBuilderStrategy {
 851         static final Unsafe UNSAFE = Unsafe.getUnsafe();
 852         static final int CLASSFILE_VERSION = 52;
 853         static final String METHOD_NAME = "concat";
 854 
 855         private BytecodeStringBuilderStrategy() {
 856             // no instantiation
 857         }
 858 
 859         private static MethodHandle generate(Lookup lookup, String className, MethodType args, Recipe recipe, Mode mode) throws Exception {
 860             ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
 861 
 862             cw.visit(CLASSFILE_VERSION,
 863                     ACC_SUPER + ACC_PUBLIC + ACC_FINAL + ACC_SYNTHETIC,
 864                     className,  // Unsafe.defineAnonymousClass would append an unique ID
 865                     null,
 866                     "java/lang/Object",
 867                     null
 868             );
 869 
 870             MethodVisitor mv = cw.visitMethod(
 871                     ACC_PUBLIC + ACC_STATIC + ACC_FINAL,
 872                     METHOD_NAME,
 873                     args.toMethodDescriptorString(),
 874                     null,
 875                     null);
 876 

 877             mv.visitAnnotation("Ljdk/internal/vm/annotation/ForceInline;", true);
 878             mv.visitCode();
 879 
 880             Class<?>[] arr = args.parameterArray();
 881             boolean[] guaranteedNonNull = new boolean[arr.length];
 882 
 883             if (mode.isExact()) {
 884                 /*
 885                     In exact mode, we need to convert all arguments to their String representations,
 886                     as this allows to compute their String sizes exactly. We cannot use private
 887                     methods for primitives in here, therefore we need to convert those as well.
 888 
 889                     We also record what arguments are guaranteed to be non-null as the result
 890                     of the conversion. String.valueOf does the null checks for us. The only
 891                     corner case to take care of is String.valueOf(Object) returning null itself.
 892 
 893                     Also, if any conversion happened, then the slot indices in the incoming
 894                     arguments are not equal to the final local maps. The only case this may break
 895                     is when converting 2-slot long/double argument to 1-slot String. Therefore,
 896                     we get away with tracking modified offset, since no conversion can overwrite


1126 
1127                 mv.visitLabel(l0);
1128             }
1129 
1130             mv.visitMethodInsn(
1131                     INVOKEVIRTUAL,
1132                     "java/lang/StringBuilder",
1133                     "toString",
1134                     "()Ljava/lang/String;",
1135                     false
1136             );
1137 
1138             mv.visitInsn(ARETURN);
1139 
1140             mv.visitMaxs(-1, -1);
1141             mv.visitEnd();
1142             cw.visitEnd();
1143 
1144             byte[] classBytes = cw.toByteArray();
1145             try {
1146                 Class<?> hostClass = lookup.lookupClass();
1147                 Class<?> innerClass = UNSAFE.defineAnonymousClass(hostClass, classBytes, null);
1148                 UNSAFE.ensureClassInitialized(innerClass);
1149                 dumpIfEnabled(innerClass.getName(), classBytes);
1150                 return Lookup.IMPL_LOOKUP.findStatic(innerClass, METHOD_NAME, args);
1151             } catch (Exception e) {
1152                 dumpIfEnabled(className + "$$FAILED", classBytes);
1153                 throw new StringConcatException("Exception while spinning the class", e);
1154             }
1155         }
1156 
1157         private static void dumpIfEnabled(String name, byte[] bytes) {
1158             if (DUMPER != null) {
1159                 DUMPER.dumpClass(name, bytes);
1160             }
1161         }
1162 
1163         private static String getSBAppendDesc(Class<?> cl) {
1164             if (cl.isPrimitive()) {
1165                 if (cl == Integer.TYPE || cl == Byte.TYPE || cl == Short.TYPE) {
1166                     return "(I)Ljava/lang/StringBuilder;";
1167                 } else if (cl == Boolean.TYPE) {
1168                     return "(Z)Ljava/lang/StringBuilder;";
1169                 } else if (cl == Character.TYPE) {
1170                     return "(C)Ljava/lang/StringBuilder;";


1253                 return 0;
1254             } else if (c == Long.TYPE || c == Double.TYPE) {
1255                 return 2;
1256             }
1257             return 1;
1258         }
1259     }
1260 
1261     /**
1262      * MethodHandle StringBuilder strategy.
1263      *
1264      * <p>This strategy operates in two modes, gated by {@link Mode}.
1265      *
1266      * <p><b>{@link Strategy#MH_SB_SIZED}: "MethodHandles StringBuilder,
1267      * sized".</b>
1268      *
1269      * <p>This strategy avoids spinning up the bytecode by building the
1270      * computation on MethodHandle combinators. The computation is built with
1271      * public MethodHandle APIs, resolved from a public Lookup sequence, and
1272      * ends up calling the public StringBuilder API. Therefore, this strategy
1273      * does not use any private API at all, even the Unsafe.defineAnonymousClass,
1274      * since everything is handled under cover by java.lang.invoke APIs.
1275      *
1276      * <p><b>{@link Strategy#MH_SB_SIZED_EXACT}: "MethodHandles StringBuilder,
1277      * sized exactly".</b>
1278      *
1279      * <p>This strategy improves on @link Strategy#MH_SB_SIZED}, by first
1280      * converting all arguments to String in order to get the exact capacity
1281      * StringBuilder should have. The conversion is done via the public
1282      * String.valueOf and/or Object.toString methods, and does not touch any
1283      * private String API.
1284      */
1285     private static final class MethodHandleStringBuilderStrategy {
1286 
1287         private MethodHandleStringBuilderStrategy() {
1288             // no instantiation
1289         }
1290 
1291         private static MethodHandle generate(MethodType mt, Recipe recipe, Mode mode) throws Exception {
1292             int pc = mt.parameterCount();
1293 
1294             Class<?>[] ptypes = mt.parameterArray();
1295             MethodHandle[] filters = new MethodHandle[ptypes.length];
1296             for (int i = 0; i < ptypes.length; i++) {
1297                 MethodHandle filter;
1298                 switch (mode) {
1299                     case SIZED:
1300                         // In sized mode, we convert all references and floats/doubles
1301                         // to String: there is no specialization for different
1302                         // classes in StringBuilder API, and it will convert to
1303                         // String internally anyhow.
1304                         filter = Stringifiers.forMost(ptypes[i]);
1305                         break;
1306                     case SIZED_EXACT:


1444         private static int sum(int v1, int v2, int v3, int v4, int v5, int v6) {
1445             return v1 + v2 + v3 + v4 + v5 + v6;
1446         }
1447 
1448         private static int sum(int v1, int v2, int v3, int v4, int v5, int v6, int v7) {
1449             return v1 + v2 + v3 + v4 + v5 + v6 + v7;
1450         }
1451 
1452         private static int sum(int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8) {
1453             return v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8;
1454         }
1455 
1456         private static int sum(int initial, int[] vs) {
1457             int sum = initial;
1458             for (int v : vs) {
1459                 sum += v;
1460             }
1461             return sum;
1462         }
1463 


1464         private static final ConcurrentMap<Integer, MethodHandle> SUMMERS;
1465 
1466         // This one is deliberately non-lambdified to optimize startup time:
1467         private static final Function<Integer, MethodHandle> SUMMER = new Function<Integer, MethodHandle>() {
1468             @Override
1469             public MethodHandle apply(Integer cnt) {
1470                 if (cnt == 1) {
1471                     return MethodHandles.identity(int.class);
1472                 } else if (cnt <= 8) {
1473                     // Variable-arity collectors are not as efficient as small-count methods,
1474                     // unroll some initial sizes.
1475                     Class<?>[] cls = new Class<?>[cnt];
1476                     Arrays.fill(cls, int.class);
1477                     return lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, cls);
1478                 } else {
1479                     return lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, int.class, int[].class)
1480                             .asCollector(int[].class, cnt - 1);
1481                 }
1482             }
1483         };
1484 
1485         private static final MethodHandle NEW_STRING_BUILDER, STRING_LENGTH, BUILDER_TO_STRING, BUILDER_TO_STRING_CHECKED;
1486 
1487         static {
1488             SUMMERS = new ConcurrentHashMap<>();
1489             Lookup publicLookup = MethodHandles.publicLookup();
1490             NEW_STRING_BUILDER = lookupConstructor(publicLookup, StringBuilder.class, int.class);
1491             STRING_LENGTH = lookupVirtual(publicLookup, String.class, "length", int.class);
1492             BUILDER_TO_STRING = lookupVirtual(publicLookup, StringBuilder.class, "toString", String.class);
1493             if (DEBUG) {
1494                 BUILDER_TO_STRING_CHECKED = lookupStatic(MethodHandles.Lookup.IMPL_LOOKUP,
1495                         MethodHandleStringBuilderStrategy.class, "toStringChecked", String.class, StringBuilder.class);
1496             } else {
1497                 BUILDER_TO_STRING_CHECKED = null;
1498             }
1499         }
1500 
1501     }
1502 
1503 
1504     /**
1505      * <p><b>{@link Strategy#MH_INLINE_SIZED_EXACT}: "MethodHandles inline,
1506      * sized exactly".</b>
1507      *
1508      * <p>This strategy replicates what StringBuilders are doing: it builds the
1509      * byte[] array on its own and passes that byte[] array to String
1510      * constructor. This strategy requires access to some private APIs in JDK,
1511      * most notably, the read-only Integer/Long.stringSize methods that measure
1512      * the character length of the integers, and the private String constructor
1513      * that accepts byte[] arrays without copying. While this strategy assumes a
1514      * particular implementation details for String, this opens the door for
1515      * building a very optimal concatenation sequence. This is the only strategy
1516      * that requires porting if there are private JDK changes occur.
1517      */
1518     private static final class MethodHandleInlineCopyStrategy {
1519         static final Unsafe UNSAFE = Unsafe.getUnsafe();
1520 
1521         private MethodHandleInlineCopyStrategy() {
1522             // no instantiation
1523         }
1524 
1525         static MethodHandle generate(MethodType mt, Recipe recipe) throws Throwable {
1526 
1527             // Fast-path two-argument Object + Object concatenations
1528             if (recipe.getElements().size() == 2) {
1529                 // Two object arguments
1530                 if (mt.parameterCount() == 2 &&
1531                     !mt.parameterType(0).isPrimitive() &&
1532                     !mt.parameterType(1).isPrimitive() &&
1533                     recipe.getElements().get(0).getTag() == TAG_ARG &&
1534                     recipe.getElements().get(1).getTag() == TAG_ARG) {
1535 
1536                     return SIMPLE;
1537 
1538                 } else if (mt.parameterCount() == 1 &&
1539                            !mt.parameterType(0).isPrimitive()) {
1540                     // One Object argument, one constant


1719                 mh = MethodHandles.filterArguments(mh, 0, filters);
1720             }
1721 
1722             return mh;
1723         }
1724 
1725         private static MethodHandle prepender(String prefix, Class<?> cl, String suffix) {
1726             return MethodHandles.insertArguments(
1727                     MethodHandles.insertArguments(
1728                         PREPENDERS.computeIfAbsent(cl, PREPEND),2, prefix), 3, suffix);
1729         }
1730 
1731         private static MethodHandle mixer(Class<?> cl) {
1732             return MIXERS.computeIfAbsent(cl, MIX);
1733         }
1734 
1735         // This one is deliberately non-lambdified to optimize startup time:
1736         private static final Function<Class<?>, MethodHandle> PREPEND = new Function<>() {
1737             @Override
1738             public MethodHandle apply(Class<?> c) {
1739                 return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "prepend", long.class, long.class, byte[].class,
1740                         String.class, Wrapper.asPrimitiveType(c), String.class);

1741             }
1742         };
1743 
1744         // This one is deliberately non-lambdified to optimize startup time:
1745         private static final Function<Class<?>, MethodHandle> MIX = new Function<>() {
1746             @Override
1747             public MethodHandle apply(Class<?> c) {
1748                 return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mix", long.class, long.class,
1749                         Wrapper.asPrimitiveType(c));
1750             }
1751         };
1752 
1753         private static final MethodHandle SIMPLE;
1754         private static final MethodHandle NEW_STRING;
1755         private static final MethodHandle NEW_ARRAY;
1756         private static final ConcurrentMap<Class<?>, MethodHandle> PREPENDERS;
1757         private static final ConcurrentMap<Class<?>, MethodHandle> MIXERS;
1758         private static final long INITIAL_CODER;
1759 
1760         static {
1761             try {
1762                 MethodHandle initCoder = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "initialCoder", long.class);
1763                 INITIAL_CODER = (long) initCoder.invoke();
1764             } catch (Throwable e) {
1765                 throw new AssertionError(e);
1766             }
1767 
1768             PREPENDERS = new ConcurrentHashMap<>();
1769             MIXERS = new ConcurrentHashMap<>();
1770 
1771             SIMPLE     = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "simpleConcat", String.class, Object.class, Object.class);
1772             NEW_STRING = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newString", String.class, byte[].class, long.class);
1773             NEW_ARRAY  = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newArray", byte[].class, long.class);
1774         }
1775     }
1776 
1777     /**
1778      * Public gateways to public "stringify" methods. These methods have the form String apply(T obj), and normally
1779      * delegate to {@code String.valueOf}, depending on argument's type.
1780      */
1781     private static final class Stringifiers {
1782         private Stringifiers() {
1783             // no instantiation
1784         }
1785 
1786         private static final MethodHandle OBJECT_INSTANCE =
1787             lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "stringOf", String.class, Object.class);
1788 
1789         private static class FloatStringifiers {
1790             private static final MethodHandle FLOAT_INSTANCE =
1791                     lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, float.class);
1792 
1793             private static final MethodHandle DOUBLE_INSTANCE =
1794                     lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, double.class);
1795         }
1796 
1797         private static class StringifierAny extends ClassValue<MethodHandle> {
1798 
1799             private static final ClassValue<MethodHandle> INSTANCE = new StringifierAny();
1800 
1801             @Override
1802             protected MethodHandle computeValue(Class<?> cl) {
1803                 if (cl == byte.class || cl == short.class || cl == int.class) {
1804                     return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, int.class);
1805                 } else if (cl == boolean.class) {
1806                     return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, boolean.class);
1807                 } else if (cl == char.class) {




   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  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.access.JavaLangAccess;
  29 import jdk.internal.access.SharedSecrets;
  30 import jdk.internal.misc.VM;
  31 import jdk.internal.org.objectweb.asm.ClassWriter;
  32 import jdk.internal.org.objectweb.asm.Label;
  33 import jdk.internal.org.objectweb.asm.MethodVisitor;
  34 import jdk.internal.org.objectweb.asm.Opcodes;
  35 import sun.invoke.util.Wrapper;
  36 
  37 import java.lang.invoke.MethodHandles.Lookup;
  38 import java.util.ArrayList;
  39 import java.util.Arrays;
  40 import java.util.List;
  41 import java.util.Objects;
  42 import java.util.concurrent.ConcurrentHashMap;
  43 import java.util.concurrent.ConcurrentMap;
  44 import java.util.function.Function;
  45 
  46 import static java.lang.invoke.MethodHandles.lookup;
  47 import static java.lang.invoke.MethodType.methodType;
  48 import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
  49 import static jdk.internal.org.objectweb.asm.Opcodes.*;
  50 
  51 /**
  52  * <p>Methods to facilitate the creation of String concatenation methods, that
  53  * can be used to efficiently concatenate a known number of arguments of known
  54  * types, possibly after type adaptation and partial evaluation of arguments.
  55  * These methods are typically used as <em>bootstrap methods</em> for {@code
  56  * invokedynamic} call sites, to support the <em>string concatenation</em>
  57  * feature of the Java Programming Language.
  58  *
  59  * <p>Indirect access to the behavior specified by the provided {@code
  60  * MethodHandle} proceeds in order through two phases:
  61  *
  62  * <ol>
  63  *     <li><em>Linkage</em> occurs when the methods in this class are invoked.
  64  * They take as arguments a method type describing the concatenated arguments
  65  * count and types, and optionally the String <em>recipe</em>, plus the
  66  * constants that participate in the String concatenation. The details on
  67  * accepted recipe shapes are described further below. Linkage may involve
  68  * dynamically loading a new class that implements the expected concatenation


 120     /**
 121      * Maximum number of argument slots in String Concat call.
 122      *
 123      * While the maximum number of argument slots that indy call can handle is 253,
 124      * we do not use all those slots, to let the strategies with MethodHandle
 125      * combinators to use some arguments.
 126      */
 127     private static final int MAX_INDY_CONCAT_ARG_SLOTS = 200;
 128 
 129     /**
 130      * Concatenation strategy to use. See {@link Strategy} for possible options.
 131      * This option is controllable with -Djava.lang.invoke.stringConcat JDK option.
 132      */
 133     private static Strategy STRATEGY;
 134 
 135     /**
 136      * Default strategy to use for concatenation.
 137      */
 138     private static final Strategy DEFAULT_STRATEGY = Strategy.MH_INLINE_SIZED_EXACT;
 139 
 140     private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
 141 
 142     private enum Strategy {
 143         /**
 144          * Bytecode generator, calling into {@link java.lang.StringBuilder}.
 145          */
 146         BC_SB,
 147 
 148         /**
 149          * Bytecode generator, calling into {@link java.lang.StringBuilder};
 150          * but trying to estimate the required storage.
 151          */
 152         BC_SB_SIZED,
 153 
 154         /**
 155          * Bytecode generator, calling into {@link java.lang.StringBuilder};
 156          * but computing the required storage exactly.
 157          */
 158         BC_SB_SIZED_EXACT,
 159 
 160         /**
 161          * MethodHandle-based generator, that in the end calls into {@link java.lang.StringBuilder}.


 178 
 179     /**
 180      * Enables debugging: this may print debugging messages, perform additional (non-neutral for performance)
 181      * checks, etc.
 182      */
 183     private static final boolean DEBUG;
 184 
 185     /**
 186      * Enables caching of strategy stubs. This may improve the linkage time by reusing the generated
 187      * code, at the expense of contaminating the profiles.
 188      */
 189     private static final boolean CACHE_ENABLE;
 190 
 191     private static final ConcurrentMap<Key, MethodHandle> CACHE;
 192 
 193     /**
 194      * Dump generated classes to disk, for debugging purposes.
 195      */
 196     private static final ProxyClassesDumper DUMPER;
 197 


 198     static {
 199         // In case we need to double-back onto the StringConcatFactory during this
 200         // static initialization, make sure we have the reasonable defaults to complete
 201         // the static initialization properly. After that, actual users would use
 202         // the proper values we have read from the properties.
 203         STRATEGY = DEFAULT_STRATEGY;
 204         // CACHE_ENABLE = false; // implied
 205         // CACHE = null;         // implied
 206         // DEBUG = false;        // implied
 207         // DUMPER = null;        // implied
 208 






 209         final String strategy =
 210                 VM.getSavedProperty("java.lang.invoke.stringConcat");
 211         CACHE_ENABLE = Boolean.parseBoolean(
 212                 VM.getSavedProperty("java.lang.invoke.stringConcat.cache"));
 213         DEBUG = Boolean.parseBoolean(
 214                 VM.getSavedProperty("java.lang.invoke.stringConcat.debug"));
 215         final String dumpPath =
 216                 VM.getSavedProperty("java.lang.invoke.stringConcat.dumpClasses");
 217 
 218         STRATEGY = (strategy == null) ? DEFAULT_STRATEGY : Strategy.valueOf(strategy);
 219         CACHE = CACHE_ENABLE ? new ConcurrentHashMap<>() : null;
 220         DUMPER = (dumpPath == null) ? null : ProxyClassesDumper.getInstance(dumpPath);
 221     }
 222 
 223     /**
 224      * Cache key is a composite of:
 225      *   - class name, that lets to disambiguate stubs, to avoid excess sharing
 226      *   - method type, describing the dynamic arguments for concatenation
 227      *   - concat recipe, describing the constants and concat shape
 228      */


 699         Class<?>[] ptypes = null;
 700         for (int i = 0; i < args.parameterCount(); i++) {
 701             Class<?> ptype = args.parameterType(i);
 702             if (!ptype.isPrimitive() &&
 703                     ptype != String.class &&
 704                     ptype != Object.class) { // truncate to Object
 705                 if (ptypes == null) {
 706                     ptypes = args.parameterArray();
 707                 }
 708                 ptypes[i] = Object.class;
 709             }
 710             // else other primitives or String or Object (unchanged)
 711         }
 712         return (ptypes != null)
 713                 ? MethodType.methodType(args.returnType(), ptypes)
 714                 : args;
 715     }
 716 
 717     private static String getClassName(Class<?> hostClass) throws StringConcatException {
 718         /*
 719           The generated class is in the same package as the host class as
 720           it's the implementation of the string concatenation for the host class.

 721 
 722           When cache is enabled, we want to cache as much as we can.














 723          */
 724 
 725         switch (STRATEGY) {
 726             case BC_SB:
 727             case BC_SB_SIZED:
 728             case BC_SB_SIZED_EXACT: {
 729                 if (CACHE_ENABLE) {
 730                     String pkgName = hostClass.getPackageName();
 731                     return (!pkgName.isEmpty() ? pkgName.replace('.', '/') + "/" : "") + "Stubs$$StringConcat";
 732                 } else {
 733                     String name = hostClass.isHidden() ? hostClass.getName().replace('/', '_')
 734                                                             : hostClass.getName();
 735                     return name.replace('.', '/') + "$$StringConcat";
 736                 }
 737             }
 738             case MH_SB_SIZED:
 739             case MH_SB_SIZED_EXACT:
 740             case MH_INLINE_SIZED_EXACT:
 741                 // MethodHandle strategies do not need a class name.
 742                 return "";
 743             default:
 744                 throw new StringConcatException("Concatenation strategy " + STRATEGY + " is not implemented");
 745         }
 746     }
 747 
 748     private static MethodHandle generate(Lookup lookup, String className, MethodType mt, Recipe recipe) throws StringConcatException {
 749         try {
 750             switch (STRATEGY) {
 751                 case BC_SB:
 752                     return BytecodeStringBuilderStrategy.generate(lookup, className, mt, recipe, Mode.DEFAULT);
 753                 case BC_SB_SIZED:
 754                     return BytecodeStringBuilderStrategy.generate(lookup, className, mt, recipe, Mode.SIZED);
 755                 case BC_SB_SIZED_EXACT:


 787         boolean isSized() {
 788             return sized;
 789         }
 790 
 791         boolean isExact() {
 792             return exact;
 793         }
 794     }
 795 
 796     /**
 797      * Bytecode StringBuilder strategy.
 798      *
 799      * <p>This strategy operates in three modes, gated by {@link Mode}.
 800      *
 801      * <p><b>{@link Strategy#BC_SB}: "bytecode StringBuilder".</b>
 802      *
 803      * <p>This strategy spins up the bytecode that has the same StringBuilder
 804      * chain javac would otherwise emit. This strategy uses only the public API,
 805      * and comes as the baseline for the current JDK behavior. On other words,
 806      * this strategy moves the javac generated bytecode to runtime. The
 807      * generated bytecode is loaded via Lookup::defineClass, but with
 808      * the caller class coming from the BSM -- in other words, the protection
 809      * guarantees are inherited from the method where invokedynamic was
 810      * originally called. This means, among other things, that the bytecode is
 811      * verified for all non-JDK uses.
 812      *
 813      * <p><b>{@link Strategy#BC_SB_SIZED}: "bytecode StringBuilder, but
 814      * sized".</b>
 815      *
 816      * <p>This strategy acts similarly to {@link Strategy#BC_SB}, but it also
 817      * tries to guess the capacity required for StringBuilder to accept all
 818      * arguments without resizing. This strategy only makes an educated guess:
 819      * it only guesses the space required for known types (e.g. primitives and
 820      * Strings), but does not otherwise convert arguments. Therefore, the
 821      * capacity estimate may be wrong, and StringBuilder may have to
 822      * transparently resize or trim when doing the actual concatenation. While
 823      * this does not constitute a correctness issue (in the end, that what BC_SB
 824      * has to do anyway), this does pose a potential performance problem.
 825      *
 826      * <p><b>{@link Strategy#BC_SB_SIZED_EXACT}: "bytecode StringBuilder, but
 827      * sized exactly".</b>
 828      *
 829      * <p>This strategy improves on @link Strategy#BC_SB_SIZED}, by first
 830      * converting all arguments to String in order to get the exact capacity
 831      * StringBuilder should have. The conversion is done via the public
 832      * String.valueOf and/or Object.toString methods, and does not touch any
 833      * private String API.
 834      */
 835     private static final class BytecodeStringBuilderStrategy {

 836         static final int CLASSFILE_VERSION = 52;
 837         static final String METHOD_NAME = "concat";
 838 
 839         private BytecodeStringBuilderStrategy() {
 840             // no instantiation
 841         }
 842 
 843         private static MethodHandle generate(Lookup lookup, String className, MethodType args, Recipe recipe, Mode mode) throws Exception {
 844             ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
 845 
 846             cw.visit(CLASSFILE_VERSION,
 847                     ACC_SUPER + ACC_PUBLIC + ACC_FINAL + ACC_SYNTHETIC,
 848                     className,
 849                     null,
 850                     "java/lang/Object",
 851                     null
 852             );
 853 
 854             MethodVisitor mv = cw.visitMethod(
 855                     ACC_PUBLIC + ACC_STATIC + ACC_FINAL,
 856                     METHOD_NAME,
 857                     args.toMethodDescriptorString(),
 858                     null,
 859                     null);
 860 
 861             // use of @ForceInline no longer has any effect
 862             mv.visitAnnotation("Ljdk/internal/vm/annotation/ForceInline;", true);
 863             mv.visitCode();
 864 
 865             Class<?>[] arr = args.parameterArray();
 866             boolean[] guaranteedNonNull = new boolean[arr.length];
 867 
 868             if (mode.isExact()) {
 869                 /*
 870                     In exact mode, we need to convert all arguments to their String representations,
 871                     as this allows to compute their String sizes exactly. We cannot use private
 872                     methods for primitives in here, therefore we need to convert those as well.
 873 
 874                     We also record what arguments are guaranteed to be non-null as the result
 875                     of the conversion. String.valueOf does the null checks for us. The only
 876                     corner case to take care of is String.valueOf(Object) returning null itself.
 877 
 878                     Also, if any conversion happened, then the slot indices in the incoming
 879                     arguments are not equal to the final local maps. The only case this may break
 880                     is when converting 2-slot long/double argument to 1-slot String. Therefore,
 881                     we get away with tracking modified offset, since no conversion can overwrite


1111 
1112                 mv.visitLabel(l0);
1113             }
1114 
1115             mv.visitMethodInsn(
1116                     INVOKEVIRTUAL,
1117                     "java/lang/StringBuilder",
1118                     "toString",
1119                     "()Ljava/lang/String;",
1120                     false
1121             );
1122 
1123             mv.visitInsn(ARETURN);
1124 
1125             mv.visitMaxs(-1, -1);
1126             mv.visitEnd();
1127             cw.visitEnd();
1128 
1129             byte[] classBytes = cw.toByteArray();
1130             try {
1131                 Class<?> innerClass = lookup.defineHiddenClass(classBytes, true, STRONG).lookupClass();
1132                 dumpIfEnabled(className, classBytes);
1133                 return lookup.findStatic(innerClass, METHOD_NAME, args);


1134             } catch (Exception e) {
1135                 dumpIfEnabled(className + "$$FAILED", classBytes);
1136                 throw new StringConcatException("Exception while spinning the class", e);
1137             }
1138         }
1139 
1140         private static void dumpIfEnabled(String name, byte[] bytes) {
1141             if (DUMPER != null) {
1142                 DUMPER.dumpClass(name, bytes);
1143             }
1144         }
1145 
1146         private static String getSBAppendDesc(Class<?> cl) {
1147             if (cl.isPrimitive()) {
1148                 if (cl == Integer.TYPE || cl == Byte.TYPE || cl == Short.TYPE) {
1149                     return "(I)Ljava/lang/StringBuilder;";
1150                 } else if (cl == Boolean.TYPE) {
1151                     return "(Z)Ljava/lang/StringBuilder;";
1152                 } else if (cl == Character.TYPE) {
1153                     return "(C)Ljava/lang/StringBuilder;";


1236                 return 0;
1237             } else if (c == Long.TYPE || c == Double.TYPE) {
1238                 return 2;
1239             }
1240             return 1;
1241         }
1242     }
1243 
1244     /**
1245      * MethodHandle StringBuilder strategy.
1246      *
1247      * <p>This strategy operates in two modes, gated by {@link Mode}.
1248      *
1249      * <p><b>{@link Strategy#MH_SB_SIZED}: "MethodHandles StringBuilder,
1250      * sized".</b>
1251      *
1252      * <p>This strategy avoids spinning up the bytecode by building the
1253      * computation on MethodHandle combinators. The computation is built with
1254      * public MethodHandle APIs, resolved from a public Lookup sequence, and
1255      * ends up calling the public StringBuilder API. Therefore, this strategy
1256      * does not use any private API at all since everything is handled under
1257      * cover by java.lang.invoke APIs.
1258      *
1259      * <p><b>{@link Strategy#MH_SB_SIZED_EXACT}: "MethodHandles StringBuilder,
1260      * sized exactly".</b>
1261      *
1262      * <p>This strategy improves on @link Strategy#MH_SB_SIZED}, by first
1263      * converting all arguments to String in order to get the exact capacity
1264      * StringBuilder should have. The conversion is done via the public
1265      * String.valueOf and/or Object.toString methods, and does not touch any
1266      * private String API.
1267      */
1268     private static final class MethodHandleStringBuilderStrategy {

1269         private MethodHandleStringBuilderStrategy() {
1270             // no instantiation
1271         }
1272 
1273         private static MethodHandle generate(MethodType mt, Recipe recipe, Mode mode) throws Exception {
1274             int pc = mt.parameterCount();
1275 
1276             Class<?>[] ptypes = mt.parameterArray();
1277             MethodHandle[] filters = new MethodHandle[ptypes.length];
1278             for (int i = 0; i < ptypes.length; i++) {
1279                 MethodHandle filter;
1280                 switch (mode) {
1281                     case SIZED:
1282                         // In sized mode, we convert all references and floats/doubles
1283                         // to String: there is no specialization for different
1284                         // classes in StringBuilder API, and it will convert to
1285                         // String internally anyhow.
1286                         filter = Stringifiers.forMost(ptypes[i]);
1287                         break;
1288                     case SIZED_EXACT:


1426         private static int sum(int v1, int v2, int v3, int v4, int v5, int v6) {
1427             return v1 + v2 + v3 + v4 + v5 + v6;
1428         }
1429 
1430         private static int sum(int v1, int v2, int v3, int v4, int v5, int v6, int v7) {
1431             return v1 + v2 + v3 + v4 + v5 + v6 + v7;
1432         }
1433 
1434         private static int sum(int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8) {
1435             return v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8;
1436         }
1437 
1438         private static int sum(int initial, int[] vs) {
1439             int sum = initial;
1440             for (int v : vs) {
1441                 sum += v;
1442             }
1443             return sum;
1444         }
1445 
1446         private static final Lookup MHSBS_LOOKUP = lookup();
1447 
1448         private static final ConcurrentMap<Integer, MethodHandle> SUMMERS;
1449 
1450         // This one is deliberately non-lambdified to optimize startup time:
1451         private static final Function<Integer, MethodHandle> SUMMER = new Function<Integer, MethodHandle>() {
1452             @Override
1453             public MethodHandle apply(Integer cnt) {
1454                 if (cnt == 1) {
1455                     return MethodHandles.identity(int.class);
1456                 } else if (cnt <= 8) {
1457                     // Variable-arity collectors are not as efficient as small-count methods,
1458                     // unroll some initial sizes.
1459                     Class<?>[] cls = new Class<?>[cnt];
1460                     Arrays.fill(cls, int.class);
1461                     return lookupStatic(MHSBS_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, cls);
1462                 } else {
1463                     return lookupStatic(MHSBS_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, int.class, int[].class)
1464                             .asCollector(int[].class, cnt - 1);
1465                 }
1466             }
1467         };
1468 
1469         private static final MethodHandle NEW_STRING_BUILDER, STRING_LENGTH, BUILDER_TO_STRING, BUILDER_TO_STRING_CHECKED;
1470 
1471         static {
1472             SUMMERS = new ConcurrentHashMap<>();
1473             Lookup publicLookup = MethodHandles.publicLookup();
1474             NEW_STRING_BUILDER = lookupConstructor(publicLookup, StringBuilder.class, int.class);
1475             STRING_LENGTH = lookupVirtual(publicLookup, String.class, "length", int.class);
1476             BUILDER_TO_STRING = lookupVirtual(publicLookup, StringBuilder.class, "toString", String.class);
1477             if (DEBUG) {
1478                 BUILDER_TO_STRING_CHECKED = lookupStatic(MHSBS_LOOKUP, MethodHandleStringBuilderStrategy.class,
1479                         "toStringChecked", String.class, StringBuilder.class);
1480             } else {
1481                 BUILDER_TO_STRING_CHECKED = null;
1482             }
1483         }
1484 
1485     }
1486 
1487 
1488     /**
1489      * <p><b>{@link Strategy#MH_INLINE_SIZED_EXACT}: "MethodHandles inline,
1490      * sized exactly".</b>
1491      *
1492      * <p>This strategy replicates what StringBuilders are doing: it builds the
1493      * byte[] array on its own and passes that byte[] array to String
1494      * constructor. This strategy requires access to some private APIs in JDK,
1495      * most notably, the read-only Integer/Long.stringSize methods that measure
1496      * the character length of the integers, and the private String constructor
1497      * that accepts byte[] arrays without copying. While this strategy assumes a
1498      * particular implementation details for String, this opens the door for
1499      * building a very optimal concatenation sequence. This is the only strategy
1500      * that requires porting if there are private JDK changes occur.
1501      */
1502     private static final class MethodHandleInlineCopyStrategy {


1503         private MethodHandleInlineCopyStrategy() {
1504             // no instantiation
1505         }
1506 
1507         static MethodHandle generate(MethodType mt, Recipe recipe) throws Throwable {
1508 
1509             // Fast-path two-argument Object + Object concatenations
1510             if (recipe.getElements().size() == 2) {
1511                 // Two object arguments
1512                 if (mt.parameterCount() == 2 &&
1513                     !mt.parameterType(0).isPrimitive() &&
1514                     !mt.parameterType(1).isPrimitive() &&
1515                     recipe.getElements().get(0).getTag() == TAG_ARG &&
1516                     recipe.getElements().get(1).getTag() == TAG_ARG) {
1517 
1518                     return SIMPLE;
1519 
1520                 } else if (mt.parameterCount() == 1 &&
1521                            !mt.parameterType(0).isPrimitive()) {
1522                     // One Object argument, one constant


1701                 mh = MethodHandles.filterArguments(mh, 0, filters);
1702             }
1703 
1704             return mh;
1705         }
1706 
1707         private static MethodHandle prepender(String prefix, Class<?> cl, String suffix) {
1708             return MethodHandles.insertArguments(
1709                     MethodHandles.insertArguments(
1710                         PREPENDERS.computeIfAbsent(cl, PREPEND),2, prefix), 3, suffix);
1711         }
1712 
1713         private static MethodHandle mixer(Class<?> cl) {
1714             return MIXERS.computeIfAbsent(cl, MIX);
1715         }
1716 
1717         // This one is deliberately non-lambdified to optimize startup time:
1718         private static final Function<Class<?>, MethodHandle> PREPEND = new Function<>() {
1719             @Override
1720             public MethodHandle apply(Class<?> c) {
1721                 return JLA.stringConcatHelper("prepend",
1722                             methodType(long.class, long.class, byte[].class,
1723                                        String.class, Wrapper.asPrimitiveType(c), String.class));
1724             }
1725         };
1726 
1727         // This one is deliberately non-lambdified to optimize startup time:
1728         private static final Function<Class<?>, MethodHandle> MIX = new Function<>() {
1729             @Override
1730             public MethodHandle apply(Class<?> c) {
1731                 return JLA.stringConcatHelper("mix", methodType(long.class, long.class, Wrapper.asPrimitiveType(c)));

1732             }
1733         };
1734 
1735         private static final MethodHandle SIMPLE;
1736         private static final MethodHandle NEW_STRING;
1737         private static final MethodHandle NEW_ARRAY;
1738         private static final ConcurrentMap<Class<?>, MethodHandle> PREPENDERS;
1739         private static final ConcurrentMap<Class<?>, MethodHandle> MIXERS;
1740         private static final long INITIAL_CODER;
1741 
1742         static {
1743             try {
1744                 MethodHandle initCoder = JLA.stringConcatHelper("initialCoder", methodType(long.class));
1745                 INITIAL_CODER = (long) initCoder.invoke();
1746             } catch (Throwable e) {
1747                 throw new AssertionError(e);
1748             }
1749 
1750             PREPENDERS = new ConcurrentHashMap<>();
1751             MIXERS = new ConcurrentHashMap<>();
1752 
1753             SIMPLE     = JLA.stringConcatHelper("simpleConcat", methodType(String.class, Object.class, Object.class));
1754             NEW_STRING = JLA.stringConcatHelper("newString", methodType(String.class, byte[].class, long.class));
1755             NEW_ARRAY  = JLA.stringConcatHelper( "newArray", methodType(byte[].class, long.class));
1756         }
1757     }
1758 
1759     /**
1760      * Public gateways to public "stringify" methods. These methods have the form String apply(T obj), and normally
1761      * delegate to {@code String.valueOf}, depending on argument's type.
1762      */
1763     private static final class Stringifiers {
1764         private Stringifiers() {
1765             // no instantiation
1766         }
1767 
1768         private static final MethodHandle OBJECT_INSTANCE =
1769                 JLA.stringConcatHelper("stringOf", methodType(String.class, Object.class));
1770 
1771         private static class FloatStringifiers {
1772             private static final MethodHandle FLOAT_INSTANCE =
1773                     lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, float.class);
1774 
1775             private static final MethodHandle DOUBLE_INSTANCE =
1776                     lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, double.class);
1777         }
1778 
1779         private static class StringifierAny extends ClassValue<MethodHandle> {
1780 
1781             private static final ClassValue<MethodHandle> INSTANCE = new StringifierAny();
1782 
1783             @Override
1784             protected MethodHandle computeValue(Class<?> cl) {
1785                 if (cl == byte.class || cl == short.class || cl == int.class) {
1786                     return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, int.class);
1787                 } else if (cl == boolean.class) {
1788                     return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, boolean.class);
1789                 } else if (cl == char.class) {


< prev index next >