1/*
2 * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
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
26package java.lang.invoke;
27
28import jdk.internal.misc.Unsafe;
29import jdk.internal.org.objectweb.asm.ClassWriter;
30import jdk.internal.org.objectweb.asm.Label;
31import jdk.internal.org.objectweb.asm.MethodVisitor;
32import jdk.internal.org.objectweb.asm.Opcodes;
33import jdk.internal.vm.annotation.ForceInline;
34import sun.invoke.util.Wrapper;
35import sun.security.action.GetPropertyAction;
36
37import java.lang.invoke.MethodHandles.Lookup;
38import java.util.ArrayList;
39import java.util.Arrays;
40import java.util.List;
41import java.util.Objects;
42import java.util.Properties;
43import java.util.concurrent.ConcurrentHashMap;
44import java.util.concurrent.ConcurrentMap;
45import java.util.function.Function;
46
47import static jdk.internal.org.objectweb.asm.Opcodes.*;
48
49/**
50 * <p>Methods to facilitate the creation of String concatenation methods, that
51 * can be used to efficiently concatenate a known number of arguments of known
52 * types, possibly after type adaptation and partial evaluation of arguments.
53 * These methods are typically used as <em>bootstrap methods</em> for {@code
54 * invokedynamic} call sites, to support the <em>string concatenation</em>
55 * feature of the Java Programming Language.
56 *
57 * <p>Indirect access to the behavior specified by the provided {@code
58 * MethodHandle} proceeds in order through two phases:
59 *
60 * <ol>
61 *     <li><em>Linkage</em> occurs when the methods in this class are invoked.
62 * They take as arguments a method type describing the concatenated arguments
63 * count and types, and optionally the String <em>recipe</em>, plus the
64 * constants that participate in the String concatenation. The details on
65 * accepted recipe shapes are described further below. Linkage may involve
66 * dynamically loading a new class that implements the expected concatenation
67 * behavior. The {@code CallSite} holds the {@code MethodHandle} pointing to the
68 * exact concatenation method. The concatenation methods may be shared among
69 * different {@code CallSite}s, e.g. if linkage methods produce them as pure
70 * functions.</li>
71 *
72 * <li><em>Invocation</em> occurs when a generated concatenation method is
73 * invoked with the exact dynamic arguments. This may occur many times for a
74 * single concatenation method. The method referenced by the behavior {@code
75 * MethodHandle} is invoked with the static arguments and any additional dynamic
76 * arguments provided on invocation, as if by {@link MethodHandle#invoke(Object...)}.</li>
77 * </ol>
78 *
79 * <p> This class provides two forms of linkage methods: a simple version
80 * ({@link #makeConcat(java.lang.invoke.MethodHandles.Lookup, String,
81 * MethodType)}) using only the dynamic arguments, and an advanced version
82 * ({@link #makeConcatWithConstants(java.lang.invoke.MethodHandles.Lookup,
83 * String, MethodType, String, Object...)} using the advanced forms of capturing
84 * the constant arguments. The advanced strategy can produce marginally better
85 * invocation bytecode, at the expense of exploding the number of shapes of
86 * string concatenation methods present at runtime, because those shapes would
87 * include constant static arguments as well.
88 *
89 * @author Aleksey Shipilev
90 * @author Remi Forax
91 * @author Peter Levart
92 *
93 * @apiNote
94 * <p>There is a JVM limit (classfile structural constraint): no method
95 * can call with more than 255 slots. This limits the number of static and
96 * dynamic arguments one can pass to bootstrap method. Since there are potential
97 * concatenation strategies that use {@code MethodHandle} combinators, we need
98 * to reserve a few empty slots on the parameter lists to capture the
99 * temporal results. This is why bootstrap methods in this factory do not accept
100 * more than 200 argument slots. Users requiring more than 200 argument slots in
101 * concatenation are expected to split the large concatenation in smaller
102 * expressions.
103 *
104 * @since 9
105 */
106public final class StringConcatFactory {
107
108    /**
109     * Tag used to demarcate an ordinary argument.
110     */
111    private static final char TAG_ARG = '\u0001';
112
113    /**
114     * Tag used to demarcate a constant.
115     */
116    private static final char TAG_CONST = '\u0002';
117
118    /**
119     * Maximum number of argument slots in String Concat call.
120     *
121     * While the maximum number of argument slots that indy call can handle is 253,
122     * we do not use all those slots, to let the strategies with MethodHandle
123     * combinators to use some arguments.
124     */
125    private static final int MAX_INDY_CONCAT_ARG_SLOTS = 200;
126
127    /**
128     * Concatenation strategy to use. See {@link Strategy} for possible options.
129     * This option is controllable with -Djava.lang.invoke.stringConcat JDK option.
130     */
131    private static Strategy STRATEGY;
132
133    /**
134     * Default strategy to use for concatenation.
135     */
136    private static final Strategy DEFAULT_STRATEGY = Strategy.MH_INLINE_SIZED_EXACT;
137
138    private enum Strategy {
139        /**
140         * Bytecode generator, calling into {@link java.lang.StringBuilder}.
141         */
142        BC_SB,
143
144        /**
145         * Bytecode generator, calling into {@link java.lang.StringBuilder};
146         * but trying to estimate the required storage.
147         */
148        BC_SB_SIZED,
149
150        /**
151         * Bytecode generator, calling into {@link java.lang.StringBuilder};
152         * but computing the required storage exactly.
153         */
154        BC_SB_SIZED_EXACT,
155
156        /**
157         * MethodHandle-based generator, that in the end calls into {@link java.lang.StringBuilder}.
158         * This strategy also tries to estimate the required storage.
159         */
160        MH_SB_SIZED,
161
162        /**
163         * MethodHandle-based generator, that in the end calls into {@link java.lang.StringBuilder}.
164         * This strategy also estimate the required storage exactly.
165         */
166        MH_SB_SIZED_EXACT,
167
168        /**
169         * MethodHandle-based generator, that constructs its own byte[] array from
170         * the arguments. It computes the required storage exactly.
171         */
172        MH_INLINE_SIZED_EXACT
173    }
174
175    /**
176     * Enables debugging: this may print debugging messages, perform additional (non-neutral for performance)
177     * checks, etc.
178     */
179    private static final boolean DEBUG;
180
181    /**
182     * Enables caching of strategy stubs. This may improve the linkage time by reusing the generated
183     * code, at the expense of contaminating the profiles.
184     */
185    private static final boolean CACHE_ENABLE;
186
187    private static final ConcurrentMap<Key, MethodHandle> CACHE;
188
189    /**
190     * Dump generated classes to disk, for debugging purposes.
191     */
192    private static final ProxyClassesDumper DUMPER;
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 the
198        // the proper values we have read from the 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        Properties props = GetPropertyAction.privilegedGetProperties();
206        final String strategy =
207                props.getProperty("java.lang.invoke.stringConcat");
208        CACHE_ENABLE = Boolean.parseBoolean(
209                props.getProperty("java.lang.invoke.stringConcat.cache"));
210        DEBUG = Boolean.parseBoolean(
211                props.getProperty("java.lang.invoke.stringConcat.debug"));
212        final String dumpPath =
213                props.getProperty("java.lang.invoke.stringConcat.dumpClasses");
214
215        STRATEGY = (strategy == null) ? DEFAULT_STRATEGY : Strategy.valueOf(strategy);
216        CACHE = CACHE_ENABLE ? new ConcurrentHashMap<>() : null;
217        DUMPER = (dumpPath == null) ? null : ProxyClassesDumper.getInstance(dumpPath);
218    }
219
220    /**
221     * Cache key is a composite of:
222     *   - class name, that lets to disambiguate stubs, to avoid excess sharing
223     *   - method type, describing the dynamic arguments for concatenation
224     *   - concat recipe, describing the constants and concat shape
225     */
226    private static final class Key {
227        final String className;
228        final MethodType mt;
229        final Recipe recipe;
230
231        public Key(String className, MethodType mt, Recipe recipe) {
232            this.className = className;
233            this.mt = mt;
234            this.recipe = recipe;
235        }
236
237        @Override
238        public boolean equals(Object o) {
239            if (this == o) return true;
240            if (o == null || getClass() != o.getClass()) return false;
241
242            Key key = (Key) o;
243
244            if (!className.equals(key.className)) return false;
245            if (!mt.equals(key.mt)) return false;
246            if (!recipe.equals(key.recipe)) return false;
247            return true;
248        }
249
250        @Override
251        public int hashCode() {
252            int result = className.hashCode();
253            result = 31 * result + mt.hashCode();
254            result = 31 * result + recipe.hashCode();
255            return result;
256        }
257    }
258
259    /**
260     * Parses the recipe string, and produces the traversable collection of
261     * {@link java.lang.invoke.StringConcatFactory.RecipeElement}-s for generator
262     * strategies. Notably, this class parses out the constants from the recipe
263     * and from other static arguments.
264     */
265    private static final class Recipe {
266        private final List<RecipeElement> elements;
267
268        public Recipe(String src, Object[] constants) {
269            List<RecipeElement> el = new ArrayList<>();
270
271            int constC = 0;
272            int argC = 0;
273
274            StringBuilder acc = new StringBuilder();
275
276            for (int i = 0; i < src.length(); i++) {
277                char c = src.charAt(i);
278
279                if (c == TAG_CONST || c == TAG_ARG) {
280                    // Detected a special tag, flush all accumulated characters
281                    // as a constant first:
282                    if (acc.length() > 0) {
283                        el.add(new RecipeElement(acc.toString()));
284                        acc.setLength(0);
285                    }
286                    if (c == TAG_CONST) {
287                        Object cnst = constants[constC++];
288                        el.add(new RecipeElement(cnst));
289                    } else if (c == TAG_ARG) {
290                        el.add(new RecipeElement(argC++));
291                    }
292                } else {
293                    // Not a special character, this is a constant embedded into
294                    // the recipe itself.
295                    acc.append(c);
296                }
297            }
298
299            // Flush the remaining characters as constant:
300            if (acc.length() > 0) {
301                el.add(new RecipeElement(acc.toString()));
302            }
303
304            elements = el;
305        }
306
307        public List<RecipeElement> getElements() {
308            return elements;
309        }
310
311        @Override
312        public boolean equals(Object o) {
313            if (this == o) return true;
314            if (o == null || getClass() != o.getClass()) return false;
315
316            Recipe recipe = (Recipe) o;
317            return elements.equals(recipe.elements);
318        }
319
320        @Override
321        public int hashCode() {
322            return elements.hashCode();
323        }
324    }
325
326    private static final class RecipeElement {
327        private final String value;
328        private final int argPos;
329        private final char tag;
330
331        public RecipeElement(Object cnst) {
332            this.value = String.valueOf(Objects.requireNonNull(cnst));
333            this.argPos = -1;
334            this.tag = TAG_CONST;
335        }
336
337        public RecipeElement(int arg) {
338            this.value = null;
339            this.argPos = arg;
340            this.tag = TAG_ARG;
341        }
342
343        public String getValue() {
344            assert (tag == TAG_CONST);
345            return value;
346        }
347
348        public int getArgPos() {
349            assert (tag == TAG_ARG);
350            return argPos;
351        }
352
353        public char getTag() {
354            return tag;
355        }
356
357        @Override
358        public boolean equals(Object o) {
359            if (this == o) return true;
360            if (o == null || getClass() != o.getClass()) return false;
361
362            RecipeElement that = (RecipeElement) o;
363
364            if (this.tag != that.tag) return false;
365            if (this.tag == TAG_CONST && (!value.equals(that.value))) return false;
366            if (this.tag == TAG_ARG && (argPos != that.argPos)) return false;
367            return true;
368        }
369
370        @Override
371        public int hashCode() {
372            return (int)tag;
373        }
374    }
375
376    /**
377     * Facilitates the creation of optimized String concatenation methods, that
378     * can be used to efficiently concatenate a known number of arguments of
379     * known types, possibly after type adaptation and partial evaluation of
380     * arguments. Typically used as a <em>bootstrap method</em> for {@code
381     * invokedynamic} call sites, to support the <em>string concatenation</em>
382     * feature of the Java Programming Language.
383     *
384     * <p>When the target of the {@code CallSite} returned from this method is
385     * invoked, it returns the result of String concatenation, taking all
386     * function arguments passed to the linkage method as inputs for
387     * concatenation. The target signature is given by {@code concatType}.
388     * The arguments are concatenated as per requirements stated in JLS 15.18.1
389     * "String Concatenation Operator +". Notably, the inputs are converted as
390     * per JLS 5.1.11 "String Conversion", and combined from left to right.
391     *
392     * <p>Assume the linkage arguments are as follows:
393     *
394     * <ul>
395     *     <li>{@code concatType}, describing the {@code CallSite} signature</li>
396     * </ul>
397     *
398     * <p>Then the following linkage invariants must hold:
399     *
400     * <ul>
401     *     <li>The parameter count in {@code concatType} is less than or equal to 200</li>
402     *
403     *     <li>The return type in {@code concatType} is assignable from {@link java.lang.String}</li>
404     * </ul>
405     *
406     * @param lookup   Represents a lookup context with the accessibility
407     *                 privileges of the caller.  When used with {@code
408     *                 invokedynamic}, this is stacked automatically by the VM.
409     * @param name     The name of the method to implement. This name is
410     *                 arbitrary, and has no meaning for this linkage method.
411     *                 When used with {@code invokedynamic}, this is provided by
412     *                 the {@code NameAndType} of the {@code InvokeDynamic}
413     *                 structure and is stacked automatically by the VM.
414     * @param concatType The expected signature of the {@code CallSite}.  The
415     *                   parameter types represent the types of concatenation
416     *                   arguments; the return type is always assignable from {@link
417     *                   java.lang.String}.  When used with {@code invokedynamic},
418     *                   this is provided by the {@code NameAndType} of the {@code
419     *                   InvokeDynamic} structure and is stacked automatically by
420     *                   the VM.
421     * @return a CallSite whose target can be used to perform String
422     * concatenation, with dynamic concatenation arguments described by the given
423     * {@code concatType}.
424     * @throws StringConcatException If any of the linkage invariants described
425     *                               here are violated.
426     * @throws NullPointerException If any of the incoming arguments is null.
427     *                              This will never happen when a bootstrap method
428     *                              is called with invokedynamic.
429     *
430     * @jls  5.1.11 String Conversion
431     * @jls 15.18.1 String Concatenation Operator +
432     */
433    public static CallSite makeConcat(MethodHandles.Lookup lookup,
434                                      String name,
435                                      MethodType concatType) throws StringConcatException {
436        if (DEBUG) {
437            System.out.println("StringConcatFactory " + STRATEGY + " is here for " + concatType);
438        }
439
440        return doStringConcat(lookup, name, concatType, true, null);
441    }
442
443    /**
444     * Facilitates the creation of optimized String concatenation methods, that
445     * can be used to efficiently concatenate a known number of arguments of
446     * known types, possibly after type adaptation and partial evaluation of
447     * arguments. Typically used as a <em>bootstrap method</em> for {@code
448     * invokedynamic} call sites, to support the <em>string concatenation</em>
449     * feature of the Java Programming Language.
450     *
451     * <p>When the target of the {@code CallSite} returned from this method is
452     * invoked, it returns the result of String concatenation, taking all
453     * function arguments and constants passed to the linkage method as inputs for
454     * concatenation. The target signature is given by {@code concatType}, and
455     * does not include constants. The arguments are concatenated as per requirements
456     * stated in JLS 15.18.1 "String Concatenation Operator +". Notably, the inputs
457     * are converted as per JLS 5.1.11 "String Conversion", and combined from left
458     * to right.
459     *
460     * <p>The concatenation <em>recipe</em> is a String description for the way to
461     * construct a concatenated String from the arguments and constants. The
462     * recipe is processed from left to right, and each character represents an
463     * input to concatenation. Recipe characters mean:
464     *
465     * <ul>
466     *
467     *   <li><em>\1 (Unicode point 0001)</em>: an ordinary argument. This
468     *   input is passed through dynamic argument, and is provided during the
469     *   concatenation method invocation. This input can be null.</li>
470     *
471     *   <li><em>\2 (Unicode point 0002):</em> a constant. This input passed
472     *   through static bootstrap argument. This constant can be any value
473     *   representable in constant pool. If necessary, the factory would call
474     *   {@code toString} to perform a one-time String conversion.</li>
475     *
476     *   <li><em>Any other char value:</em> a single character constant.</li>
477     * </ul>
478     *
479     * <p>Assume the linkage arguments are as follows:
480     *
481     * <ul>
482     *   <li>{@code concatType}, describing the {@code CallSite} signature</li>
483     *   <li>{@code recipe}, describing the String recipe</li>
484     *   <li>{@code constants}, the vararg array of constants</li>
485     * </ul>
486     *
487     * <p>Then the following linkage invariants must hold:
488     *
489     * <ul>
490     *   <li>The parameter count in {@code concatType} is less than or equal to
491     *   200</li>
492     *
493     *   <li>The parameter count in {@code concatType} equals to number of \1 tags
494     *   in {@code recipe}</li>
495     *
496     *   <li>The return type in {@code concatType} is assignable
497     *   from {@link java.lang.String}, and matches the return type of the
498     *   returned {@link MethodHandle}</li>
499     *
500     *   <li>The number of elements in {@code constants} equals to number of \2
501     *   tags in {@code recipe}</li>
502     * </ul>
503     *
504     * @param lookup    Represents a lookup context with the accessibility
505     *                  privileges of the caller. When used with {@code
506     *                  invokedynamic}, this is stacked automatically by the
507     *                  VM.
508     * @param name      The name of the method to implement. This name is
509     *                  arbitrary, and has no meaning for this linkage method.
510     *                  When used with {@code invokedynamic}, this is provided
511     *                  by the {@code NameAndType} of the {@code InvokeDynamic}
512     *                  structure and is stacked automatically by the VM.
513     * @param concatType The expected signature of the {@code CallSite}.  The
514     *                  parameter types represent the types of dynamic concatenation
515     *                  arguments; the return type is always assignable from {@link
516     *                  java.lang.String}.  When used with {@code
517     *                  invokedynamic}, this is provided by the {@code
518     *                  NameAndType} of the {@code InvokeDynamic} structure and
519     *                  is stacked automatically by the VM.
520     * @param recipe    Concatenation recipe, described above.
521     * @param constants A vararg parameter representing the constants passed to
522     *                  the linkage method.
523     * @return a CallSite whose target can be used to perform String
524     * concatenation, with dynamic concatenation arguments described by the given
525     * {@code concatType}.
526     * @throws StringConcatException If any of the linkage invariants described
527     *                               here are violated.
528     * @throws NullPointerException If any of the incoming arguments is null, or
529     *                              any constant in {@code recipe} is null.
530     *                              This will never happen when a bootstrap method
531     *                              is called with invokedynamic.
532     * @apiNote Code generators have three distinct ways to process a constant
533     * string operand S in a string concatenation expression.  First, S can be
534     * materialized as a reference (using ldc) and passed as an ordinary argument
535     * (recipe '\1'). Or, S can be stored in the constant pool and passed as a
536     * constant (recipe '\2') . Finally, if S contains neither of the recipe
537     * tag characters ('\1', '\2') then S can be interpolated into the recipe
538     * itself, causing its characters to be inserted into the result.
539     *
540     * @jls  5.1.11 String Conversion
541     * @jls 15.18.1 String Concatenation Operator +
542     */
543    public static CallSite makeConcatWithConstants(MethodHandles.Lookup lookup,
544                                                   String name,
545                                                   MethodType concatType,
546                                                   String recipe,
547                                                   Object... constants) throws StringConcatException {
548        if (DEBUG) {
549            System.out.println("StringConcatFactory " + STRATEGY + " is here for " + concatType + ", {" + recipe + "}, " + Arrays.toString(constants));
550        }
551
552        return doStringConcat(lookup, name, concatType, false, recipe, constants);
553    }
554
555    private static CallSite doStringConcat(MethodHandles.Lookup lookup,
556                                           String name,
557                                           MethodType concatType,
558                                           boolean generateRecipe,
559                                           String recipe,
560                                           Object... constants) throws StringConcatException {
561        Objects.requireNonNull(lookup, "Lookup is null");
562        Objects.requireNonNull(name, "Name is null");
563        Objects.requireNonNull(concatType, "Concat type is null");
564        Objects.requireNonNull(constants, "Constants are null");
565
566        for (Object o : constants) {
567            Objects.requireNonNull(o, "Cannot accept null constants");
568        }
569
570        if ((lookup.lookupModes() & MethodHandles.Lookup.PRIVATE) == 0) {
571            throw new StringConcatException("Invalid caller: " +
572                    lookup.lookupClass().getName());
573        }
574
575        int cCount = 0;
576        int oCount = 0;
577        if (generateRecipe) {
578            // Mock the recipe to reuse the concat generator code
579            char[] value = new char[concatType.parameterCount()];
580            Arrays.fill(value, TAG_ARG);
581            recipe = new String(value);
582            oCount = concatType.parameterCount();
583        } else {
584            Objects.requireNonNull(recipe, "Recipe is null");
585
586            for (int i = 0; i < recipe.length(); i++) {
587                char c = recipe.charAt(i);
588                if (c == TAG_CONST) cCount++;
589                if (c == TAG_ARG)   oCount++;
590            }
591        }
592
593        if (oCount != concatType.parameterCount()) {
594            throw new StringConcatException(
595                    "Mismatched number of concat arguments: recipe wants " +
596                            oCount +
597                            " arguments, but signature provides " +
598                            concatType.parameterCount());
599        }
600
601        if (cCount != constants.length) {
602            throw new StringConcatException(
603                    "Mismatched number of concat constants: recipe wants " +
604                            cCount +
605                            " constants, but only " +
606                            constants.length +
607                            " are passed");
608        }
609
610        if (!concatType.returnType().isAssignableFrom(String.class)) {
611            throw new StringConcatException(
612                    "The return type should be compatible with String, but it is " +
613                            concatType.returnType());
614        }
615
616        if (concatType.parameterCount() > MAX_INDY_CONCAT_ARG_SLOTS) {
617            throw new StringConcatException("Too many concat argument slots: " +
618                    concatType.parameterCount() +
619                    ", can only accept " +
620                    MAX_INDY_CONCAT_ARG_SLOTS);
621        }
622
623        String className = getClassName(lookup.lookupClass());
624        MethodType mt = adaptType(concatType);
625        Recipe rec = new Recipe(recipe, constants);
626
627        MethodHandle mh;
628        if (CACHE_ENABLE) {
629            Key key = new Key(className, mt, rec);
630            mh = CACHE.get(key);
631            if (mh == null) {
632                mh = generate(lookup, className, mt, rec);
633                CACHE.put(key, mh);
634            }
635        } else {
636            mh = generate(lookup, className, mt, rec);
637        }
638        return new ConstantCallSite(mh.asType(concatType));
639    }
640
641    /**
642     * Adapt method type to an API we are going to use.
643     *
644     * This strips the concrete classes from the signatures, thus preventing
645     * class leakage when we cache the concatenation stubs.
646     *
647     * @param args actual argument types
648     * @return argument types the strategy is going to use
649     */
650    private static MethodType adaptType(MethodType args) {
651        Class<?>[] ptypes = null;
652        for (int i = 0; i < args.parameterCount(); i++) {
653            Class<?> ptype = args.parameterType(i);
654            if (!ptype.isPrimitive() &&
655                    ptype != String.class &&
656                    ptype != Object.class) { // truncate to Object
657                if (ptypes == null) {
658                    ptypes = args.parameterArray();
659                }
660                ptypes[i] = Object.class;
661            }
662            // else other primitives or String or Object (unchanged)
663        }
664        return (ptypes != null)
665                ? MethodType.methodType(args.returnType(), ptypes)
666                : args;
667    }
668
669    private static String getClassName(Class<?> hostClass) throws StringConcatException {
670        /*
671          When cache is enabled, we want to cache as much as we can.
672
673          However, there are two peculiarities:
674
675           a) The generated class should stay within the same package as the
676              host class, to allow Unsafe.defineAnonymousClass access controls
677              to work properly. JDK may choose to fail with IllegalAccessException
678              when accessing a VM anonymous class with non-privileged callers,
679              see JDK-8058575.
680
681           b) If we mark the stub with some prefix, say, derived from the package
682              name because of (a), we can technically use that stub in other packages.
683              But the call stack traces would be extremely puzzling to unsuspecting users
684              and profiling tools: whatever stub wins the race, would be linked in all
685              similar callsites.
686
687           Therefore, we set the class prefix to match the host class package, and use
688           the prefix as the cache key too. This only affects BC_* strategies, and only when
689           cache is enabled.
690         */
691
692        switch (STRATEGY) {
693            case BC_SB:
694            case BC_SB_SIZED:
695            case BC_SB_SIZED_EXACT: {
696                if (CACHE_ENABLE) {
697                    String pkgName = hostClass.getPackageName();
698                    return (pkgName != null && !pkgName.isEmpty() ? pkgName.replace('.', '/') + "/" : "") + "Stubs$$StringConcat";
699                } else {
700                    return hostClass.getName().replace('.', '/') + "$$StringConcat";
701                }
702            }
703            case MH_SB_SIZED:
704            case MH_SB_SIZED_EXACT:
705            case MH_INLINE_SIZED_EXACT:
706                // MethodHandle strategies do not need a class name.
707                return "";
708            default:
709                throw new StringConcatException("Concatenation strategy " + STRATEGY + " is not implemented");
710        }
711    }
712
713    private static MethodHandle generate(Lookup lookup, String className, MethodType mt, Recipe recipe) throws StringConcatException {
714        try {
715            switch (STRATEGY) {
716                case BC_SB:
717                    return BytecodeStringBuilderStrategy.generate(lookup, className, mt, recipe, Mode.DEFAULT);
718                case BC_SB_SIZED:
719                    return BytecodeStringBuilderStrategy.generate(lookup, className, mt, recipe, Mode.SIZED);
720                case BC_SB_SIZED_EXACT:
721                    return BytecodeStringBuilderStrategy.generate(lookup, className, mt, recipe, Mode.SIZED_EXACT);
722                case MH_SB_SIZED:
723                    return MethodHandleStringBuilderStrategy.generate(mt, recipe, Mode.SIZED);
724                case MH_SB_SIZED_EXACT:
725                    return MethodHandleStringBuilderStrategy.generate(mt, recipe, Mode.SIZED_EXACT);
726                case MH_INLINE_SIZED_EXACT:
727                    return MethodHandleInlineCopyStrategy.generate(mt, recipe);
728                default:
729                    throw new StringConcatException("Concatenation strategy " + STRATEGY + " is not implemented");
730            }
731        } catch (Error | StringConcatException e) {
732            // Pass through any error or existing StringConcatException
733            throw e;
734        } catch (Throwable t) {
735            throw new StringConcatException("Generator failed", t);
736        }
737    }
738
739    private enum Mode {
740        DEFAULT(false, false),
741        SIZED(true, false),
742        SIZED_EXACT(true, true);
743
744        private final boolean sized;
745        private final boolean exact;
746
747        Mode(boolean sized, boolean exact) {
748            this.sized = sized;
749            this.exact = exact;
750        }
751
752        boolean isSized() {
753            return sized;
754        }
755
756        boolean isExact() {
757            return exact;
758        }
759    }
760
761    /**
762     * Bytecode StringBuilder strategy.
763     *
764     * <p>This strategy operates in three modes, gated by {@link Mode}.
765     *
766     * <p><b>{@link Strategy#BC_SB}: "bytecode StringBuilder".</b>
767     *
768     * <p>This strategy spins up the bytecode that has the same StringBuilder
769     * chain javac would otherwise emit. This strategy uses only the public API,
770     * and comes as the baseline for the current JDK behavior. On other words,
771     * this strategy moves the javac generated bytecode to runtime. The
772     * generated bytecode is loaded via Unsafe.defineAnonymousClass, but with
773     * the caller class coming from the BSM -- in other words, the protection
774     * guarantees are inherited from the method where invokedynamic was
775     * originally called. This means, among other things, that the bytecode is
776     * verified for all non-JDK uses.
777     *
778     * <p><b>{@link Strategy#BC_SB_SIZED}: "bytecode StringBuilder, but
779     * sized".</b>
780     *
781     * <p>This strategy acts similarly to {@link Strategy#BC_SB}, but it also
782     * tries to guess the capacity required for StringBuilder to accept all
783     * arguments without resizing. This strategy only makes an educated guess:
784     * it only guesses the space required for known types (e.g. primitives and
785     * Strings), but does not otherwise convert arguments. Therefore, the
786     * capacity estimate may be wrong, and StringBuilder may have to
787     * transparently resize or trim when doing the actual concatenation. While
788     * this does not constitute a correctness issue (in the end, that what BC_SB
789     * has to do anyway), this does pose a potential performance problem.
790     *
791     * <p><b>{@link Strategy#BC_SB_SIZED_EXACT}: "bytecode StringBuilder, but
792     * sized exactly".</b>
793     *
794     * <p>This strategy improves on @link Strategy#BC_SB_SIZED}, by first
795     * converting all arguments to String in order to get the exact capacity
796     * StringBuilder should have. The conversion is done via the public
797     * String.valueOf and/or Object.toString methods, and does not touch any
798     * private String API.
799     */
800    private static final class BytecodeStringBuilderStrategy {
801        static final Unsafe UNSAFE = Unsafe.getUnsafe();
802        static final int CLASSFILE_VERSION = 52;
803        static final String METHOD_NAME = "concat";
804
805        private BytecodeStringBuilderStrategy() {
806            // no instantiation
807        }
808
809        private static MethodHandle generate(Lookup lookup, String className, MethodType args, Recipe recipe, Mode mode) throws Exception {
810            ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
811
812            cw.visit(CLASSFILE_VERSION,
813                    ACC_SUPER + ACC_PUBLIC + ACC_FINAL + ACC_SYNTHETIC,
814                    className,  // Unsafe.defineAnonymousClass would append an unique ID
815                    null,
816                    "java/lang/Object",
817                    null
818            );
819
820            MethodVisitor mv = cw.visitMethod(
821                    ACC_PUBLIC + ACC_STATIC + ACC_FINAL,
822                    METHOD_NAME,
823                    args.toMethodDescriptorString(),
824                    null,
825                    null);
826
827            mv.visitAnnotation("Ljdk/internal/vm/annotation/ForceInline;", true);
828            mv.visitCode();
829
830            Class<?>[] arr = args.parameterArray();
831            boolean[] guaranteedNonNull = new boolean[arr.length];
832
833            if (mode.isExact()) {
834                /*
835                    In exact mode, we need to convert all arguments to their String representations,
836                    as this allows to compute their String sizes exactly. We cannot use private
837                    methods for primitives in here, therefore we need to convert those as well.
838
839                    We also record what arguments are guaranteed to be non-null as the result
840                    of the conversion. String.valueOf does the null checks for us. The only
841                    corner case to take care of is String.valueOf(Object) returning null itself.
842
843                    Also, if any conversion happened, then the slot indices in the incoming
844                    arguments are not equal to the final local maps. The only case this may break
845                    is when converting 2-slot long/double argument to 1-slot String. Therefore,
846                    we get away with tracking modified offset, since no conversion can overwrite
847                    the upcoming the argument.
848                 */
849
850                int off = 0;
851                int modOff = 0;
852                for (int c = 0; c < arr.length; c++) {
853                    Class<?> cl = arr[c];
854                    if (cl == String.class) {
855                        if (off != modOff) {
856                            mv.visitIntInsn(getLoadOpcode(cl), off);
857                            mv.visitIntInsn(ASTORE, modOff);
858                        }
859                    } else {
860                        mv.visitIntInsn(getLoadOpcode(cl), off);
861                        mv.visitMethodInsn(
862                                INVOKESTATIC,
863                                "java/lang/String",
864                                "valueOf",
865                                getStringValueOfDesc(cl),
866                                false
867                        );
868                        mv.visitIntInsn(ASTORE, modOff);
869                        arr[c] = String.class;
870                        guaranteedNonNull[c] = cl.isPrimitive();
871                    }
872                    off += getParameterSize(cl);
873                    modOff += getParameterSize(String.class);
874                }
875            }
876
877            if (mode.isSized()) {
878                /*
879                    When operating in sized mode (this includes exact mode), it makes sense to make
880                    StringBuilder append chains look familiar to OptimizeStringConcat. For that, we
881                    need to do null-checks early, not make the append chain shape simpler.
882                 */
883
884                int off = 0;
885                for (RecipeElement el : recipe.getElements()) {
886                    switch (el.getTag()) {
887                        case TAG_CONST:
888                            // Guaranteed non-null, no null check required.
889                            break;
890                        case TAG_ARG:
891                            // Null-checks are needed only for String arguments, and when a previous stage
892                            // did not do implicit null-checks. If a String is null, we eagerly replace it
893                            // with "null" constant. Note, we omit Objects here, because we don't call
894                            // .length() on them down below.
895                            int ac = el.getArgPos();
896                            Class<?> cl = arr[ac];
897                            if (cl == String.class && !guaranteedNonNull[ac]) {
898                                Label l0 = new Label();
899                                mv.visitIntInsn(ALOAD, off);
900                                mv.visitJumpInsn(IFNONNULL, l0);
901                                mv.visitLdcInsn("null");
902                                mv.visitIntInsn(ASTORE, off);
903                                mv.visitLabel(l0);
904                            }
905                            off += getParameterSize(cl);
906                            break;
907                        default:
908                            throw new StringConcatException("Unhandled tag: " + el.getTag());
909                    }
910                }
911            }
912
913            // Prepare StringBuilder instance
914            mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
915            mv.visitInsn(DUP);
916
917            if (mode.isSized()) {
918                /*
919                    Sized mode requires us to walk through the arguments, and estimate the final length.
920                    In exact mode, this will operate on Strings only. This code would accumulate the
921                    final length on stack.
922                 */
923                int len = 0;
924                int off = 0;
925
926                mv.visitInsn(ICONST_0);
927
928                for (RecipeElement el : recipe.getElements()) {
929                    switch (el.getTag()) {
930                        case TAG_CONST:
931                            len += el.getValue().length();
932                            break;
933                        case TAG_ARG:
934                            /*
935                                If an argument is String, then we can call .length() on it. Sized/Exact modes have
936                                converted arguments for us. If an argument is primitive, we can provide a guess
937                                for its String representation size.
938                            */
939                            Class<?> cl = arr[el.getArgPos()];
940                            if (cl == String.class) {
941                                mv.visitIntInsn(ALOAD, off);
942                                mv.visitMethodInsn(
943                                        INVOKEVIRTUAL,
944                                        "java/lang/String",
945                                        "length",
946                                        "()I",
947                                        false
948                                );
949                                mv.visitInsn(IADD);
950                            } else if (cl.isPrimitive()) {
951                                len += estimateSize(cl);
952                            }
953                            off += getParameterSize(cl);
954                            break;
955                        default:
956                            throw new StringConcatException("Unhandled tag: " + el.getTag());
957                    }
958                }
959
960                // Constants have non-zero length, mix in
961                if (len > 0) {
962                    iconst(mv, len);
963                    mv.visitInsn(IADD);
964                }
965
966                mv.visitMethodInsn(
967                        INVOKESPECIAL,
968                        "java/lang/StringBuilder",
969                        "<init>",
970                        "(I)V",
971                        false
972                );
973            } else {
974                mv.visitMethodInsn(
975                        INVOKESPECIAL,
976                        "java/lang/StringBuilder",
977                        "<init>",
978                        "()V",
979                        false
980                );
981            }
982
983            // At this point, we have a blank StringBuilder on stack, fill it in with .append calls.
984            {
985                int off = 0;
986                for (RecipeElement el : recipe.getElements()) {
987                    String desc;
988                    switch (el.getTag()) {
989                        case TAG_CONST:
990                            mv.visitLdcInsn(el.getValue());
991                            desc = getSBAppendDesc(String.class);
992                            break;
993                        case TAG_ARG:
994                            Class<?> cl = arr[el.getArgPos()];
995                            mv.visitVarInsn(getLoadOpcode(cl), off);
996                            off += getParameterSize(cl);
997                            desc = getSBAppendDesc(cl);
998                            break;
999                        default:
1000                            throw new StringConcatException("Unhandled tag: " + el.getTag());
1001                    }
1002
1003                    mv.visitMethodInsn(
1004                            INVOKEVIRTUAL,
1005                            "java/lang/StringBuilder",
1006                            "append",
1007                            desc,
1008                            false
1009                    );
1010                }
1011            }
1012
1013            if (DEBUG && mode.isExact()) {
1014                /*
1015                    Exactness checks compare the final StringBuilder.capacity() with a resulting
1016                    String.length(). If these values disagree, that means StringBuilder had to perform
1017                    storage trimming, which defeats the purpose of exact strategies.
1018                 */
1019
1020                /*
1021                   The logic for this check is as follows:
1022
1023                     Stack before:     Op:
1024                      (SB)              dup, dup
1025                      (SB, SB, SB)      capacity()
1026                      (int, SB, SB)     swap
1027                      (SB, int, SB)     toString()
1028                      (S, int, SB)      length()
1029                      (int, int, SB)    if_icmpeq
1030                      (SB)              <end>
1031
1032                   Note that it leaves the same StringBuilder on exit, like the one on enter.
1033                 */
1034
1035                mv.visitInsn(DUP);
1036                mv.visitInsn(DUP);
1037
1038                mv.visitMethodInsn(
1039                        INVOKEVIRTUAL,
1040                        "java/lang/StringBuilder",
1041                        "capacity",
1042                        "()I",
1043                        false
1044                );
1045
1046                mv.visitInsn(SWAP);
1047
1048                mv.visitMethodInsn(
1049                        INVOKEVIRTUAL,
1050                        "java/lang/StringBuilder",
1051                        "toString",
1052                        "()Ljava/lang/String;",
1053                        false
1054                );
1055
1056                mv.visitMethodInsn(
1057                        INVOKEVIRTUAL,
1058                        "java/lang/String",
1059                        "length",
1060                        "()I",
1061                        false
1062                );
1063
1064                Label l0 = new Label();
1065                mv.visitJumpInsn(IF_ICMPEQ, l0);
1066
1067                mv.visitTypeInsn(NEW, "java/lang/AssertionError");
1068                mv.visitInsn(DUP);
1069                mv.visitLdcInsn("Failed exactness check");
1070                mv.visitMethodInsn(INVOKESPECIAL,
1071                        "java/lang/AssertionError",
1072                        "<init>",
1073                        "(Ljava/lang/Object;)V",
1074                        false);
1075                mv.visitInsn(ATHROW);
1076
1077                mv.visitLabel(l0);
1078            }
1079
1080            mv.visitMethodInsn(
1081                    INVOKEVIRTUAL,
1082                    "java/lang/StringBuilder",
1083                    "toString",
1084                    "()Ljava/lang/String;",
1085                    false
1086            );
1087
1088            mv.visitInsn(ARETURN);
1089
1090            mv.visitMaxs(-1, -1);
1091            mv.visitEnd();
1092            cw.visitEnd();
1093
1094            byte[] classBytes = cw.toByteArray();
1095            try {
1096                Class<?> hostClass = lookup.lookupClass();
1097                Class<?> innerClass = UNSAFE.defineAnonymousClass(hostClass, classBytes, null);
1098                UNSAFE.ensureClassInitialized(innerClass);
1099                dumpIfEnabled(innerClass.getName(), classBytes);
1100                return Lookup.IMPL_LOOKUP.findStatic(innerClass, METHOD_NAME, args);
1101            } catch (Exception e) {
1102                dumpIfEnabled(className + "$$FAILED", classBytes);
1103                throw new StringConcatException("Exception while spinning the class", e);
1104            }
1105        }
1106
1107        private static void dumpIfEnabled(String name, byte[] bytes) {
1108            if (DUMPER != null) {
1109                DUMPER.dumpClass(name, bytes);
1110            }
1111        }
1112
1113        private static String getSBAppendDesc(Class<?> cl) {
1114            if (cl.isPrimitive()) {
1115                if (cl == Integer.TYPE || cl == Byte.TYPE || cl == Short.TYPE) {
1116                    return "(I)Ljava/lang/StringBuilder;";
1117                } else if (cl == Boolean.TYPE) {
1118                    return "(Z)Ljava/lang/StringBuilder;";
1119                } else if (cl == Character.TYPE) {
1120                    return "(C)Ljava/lang/StringBuilder;";
1121                } else if (cl == Double.TYPE) {
1122                    return "(D)Ljava/lang/StringBuilder;";
1123                } else if (cl == Float.TYPE) {
1124                    return "(F)Ljava/lang/StringBuilder;";
1125                } else if (cl == Long.TYPE) {
1126                    return "(J)Ljava/lang/StringBuilder;";
1127                } else {
1128                    throw new IllegalStateException("Unhandled primitive StringBuilder.append: " + cl);
1129                }
1130            } else if (cl == String.class) {
1131                return "(Ljava/lang/String;)Ljava/lang/StringBuilder;";
1132            } else {
1133                return "(Ljava/lang/Object;)Ljava/lang/StringBuilder;";
1134            }
1135        }
1136
1137        private static String getStringValueOfDesc(Class<?> cl) {
1138            if (cl.isPrimitive()) {
1139                if (cl == Integer.TYPE || cl == Byte.TYPE || cl == Short.TYPE) {
1140                    return "(I)Ljava/lang/String;";
1141                } else if (cl == Boolean.TYPE) {
1142                    return "(Z)Ljava/lang/String;";
1143                } else if (cl == Character.TYPE) {
1144                    return "(C)Ljava/lang/String;";
1145                } else if (cl == Double.TYPE) {
1146                    return "(D)Ljava/lang/String;";
1147                } else if (cl == Float.TYPE) {
1148                    return "(F)Ljava/lang/String;";
1149                } else if (cl == Long.TYPE) {
1150                    return "(J)Ljava/lang/String;";
1151                } else {
1152                    throw new IllegalStateException("Unhandled String.valueOf: " + cl);
1153                }
1154            } else if (cl == String.class) {
1155                return "(Ljava/lang/String;)Ljava/lang/String;";
1156            } else {
1157                return "(Ljava/lang/Object;)Ljava/lang/String;";
1158            }
1159        }
1160
1161        /**
1162         * The following method is copied from
1163         * org.objectweb.asm.commons.InstructionAdapter. Part of ASM: a very small
1164         * and fast Java bytecode manipulation framework.
1165         * Copyright (c) 2000-2005 INRIA, France Telecom All rights reserved.
1166         */
1167        private static void iconst(MethodVisitor mv, final int cst) {
1168            if (cst >= -1 && cst <= 5) {
1169                mv.visitInsn(Opcodes.ICONST_0 + cst);
1170            } else if (cst >= Byte.MIN_VALUE && cst <= Byte.MAX_VALUE) {
1171                mv.visitIntInsn(Opcodes.BIPUSH, cst);
1172            } else if (cst >= Short.MIN_VALUE && cst <= Short.MAX_VALUE) {
1173                mv.visitIntInsn(Opcodes.SIPUSH, cst);
1174            } else {
1175                mv.visitLdcInsn(cst);
1176            }
1177        }
1178
1179        private static int getLoadOpcode(Class<?> c) {
1180            if (c == Void.TYPE) {
1181                throw new InternalError("Unexpected void type of load opcode");
1182            }
1183            return ILOAD + getOpcodeOffset(c);
1184        }
1185
1186        private static int getOpcodeOffset(Class<?> c) {
1187            if (c.isPrimitive()) {
1188                if (c == Long.TYPE) {
1189                    return 1;
1190                } else if (c == Float.TYPE) {
1191                    return 2;
1192                } else if (c == Double.TYPE) {
1193                    return 3;
1194                }
1195                return 0;
1196            } else {
1197                return 4;
1198            }
1199        }
1200
1201        private static int getParameterSize(Class<?> c) {
1202            if (c == Void.TYPE) {
1203                return 0;
1204            } else if (c == Long.TYPE || c == Double.TYPE) {
1205                return 2;
1206            }
1207            return 1;
1208        }
1209    }
1210
1211    /**
1212     * MethodHandle StringBuilder strategy.
1213     *
1214     * <p>This strategy operates in two modes, gated by {@link Mode}.
1215     *
1216     * <p><b>{@link Strategy#MH_SB_SIZED}: "MethodHandles StringBuilder,
1217     * sized".</b>
1218     *
1219     * <p>This strategy avoids spinning up the bytecode by building the
1220     * computation on MethodHandle combinators. The computation is built with
1221     * public MethodHandle APIs, resolved from a public Lookup sequence, and
1222     * ends up calling the public StringBuilder API. Therefore, this strategy
1223     * does not use any private API at all, even the Unsafe.defineAnonymousClass,
1224     * since everything is handled under cover by java.lang.invoke APIs.
1225     *
1226     * <p><b>{@link Strategy#MH_SB_SIZED_EXACT}: "MethodHandles StringBuilder,
1227     * sized exactly".</b>
1228     *
1229     * <p>This strategy improves on @link Strategy#MH_SB_SIZED}, by first
1230     * converting all arguments to String in order to get the exact capacity
1231     * StringBuilder should have. The conversion is done via the public
1232     * String.valueOf and/or Object.toString methods, and does not touch any
1233     * private String API.
1234     */
1235    private static final class MethodHandleStringBuilderStrategy {
1236
1237        private MethodHandleStringBuilderStrategy() {
1238            // no instantiation
1239        }
1240
1241        private static MethodHandle generate(MethodType mt, Recipe recipe, Mode mode) throws Exception {
1242            int pc = mt.parameterCount();
1243
1244            Class<?>[] ptypes = mt.parameterArray();
1245            MethodHandle[] filters = new MethodHandle[ptypes.length];
1246            for (int i = 0; i < ptypes.length; i++) {
1247                MethodHandle filter;
1248                switch (mode) {
1249                    case SIZED:
1250                        // In sized mode, we convert all references and floats/doubles
1251                        // to String: there is no specialization for different
1252                        // classes in StringBuilder API, and it will convert to
1253                        // String internally anyhow.
1254                        filter = Stringifiers.forMost(ptypes[i]);
1255                        break;
1256                    case SIZED_EXACT:
1257                        // In exact mode, we convert everything to String:
1258                        // this helps to compute the storage exactly.
1259                        filter = Stringifiers.forAny(ptypes[i]);
1260                        break;
1261                    default:
1262                        throw new StringConcatException("Not supported");
1263                }
1264                if (filter != null) {
1265                    filters[i] = filter;
1266                    ptypes[i] = filter.type().returnType();
1267                }
1268            }
1269
1270            MethodHandle[] lengthers = new MethodHandle[pc];
1271
1272            // Figure out lengths: constants' lengths can be deduced on the spot.
1273            // All reference arguments were filtered to String in the combinators below, so we can
1274            // call the usual String.length(). Primitive values string sizes can be estimated.
1275            int initial = 0;
1276            for (RecipeElement el : recipe.getElements()) {
1277                switch (el.getTag()) {
1278                    case TAG_CONST:
1279                        initial += el.getValue().length();
1280                        break;
1281                    case TAG_ARG:
1282                        final int i = el.getArgPos();
1283                        Class<?> type = ptypes[i];
1284                        if (type.isPrimitive()) {
1285                            MethodHandle est = MethodHandles.constant(int.class, estimateSize(type));
1286                            est = MethodHandles.dropArguments(est, 0, type);
1287                            lengthers[i] = est;
1288                        } else {
1289                            lengthers[i] = STRING_LENGTH;
1290                        }
1291                        break;
1292                    default:
1293                        throw new StringConcatException("Unhandled tag: " + el.getTag());
1294                }
1295            }
1296
1297            // Create (StringBuilder, <args>) shape for appending:
1298            MethodHandle builder = MethodHandles.dropArguments(MethodHandles.identity(StringBuilder.class), 1, ptypes);
1299
1300            // Compose append calls. This is done in reverse because the application order is
1301            // reverse as well.
1302            List<RecipeElement> elements = recipe.getElements();
1303            for (int i = elements.size() - 1; i >= 0; i--) {
1304                RecipeElement el = elements.get(i);
1305                MethodHandle appender;
1306                switch (el.getTag()) {
1307                    case TAG_CONST:
1308                        MethodHandle mh = appender(adaptToStringBuilder(String.class));
1309                        appender = MethodHandles.insertArguments(mh, 1, el.getValue());
1310                        break;
1311                    case TAG_ARG:
1312                        int ac = el.getArgPos();
1313                        appender = appender(ptypes[ac]);
1314
1315                        // Insert dummy arguments to match the prefix in the signature.
1316                        // The actual appender argument will be the ac-ith argument.
1317                        if (ac != 0) {
1318                            appender = MethodHandles.dropArguments(appender, 1, Arrays.copyOf(ptypes, ac));
1319                        }
1320                        break;
1321                    default:
1322                        throw new StringConcatException("Unhandled tag: " + el.getTag());
1323                }
1324                builder = MethodHandles.foldArguments(builder, appender);
1325            }
1326
1327            // Build the sub-tree that adds the sizes and produces a StringBuilder:
1328
1329            // a) Start with the reducer that accepts all arguments, plus one
1330            //    slot for the initial value. Inject the initial value right away.
1331            //    This produces (<ints>)int shape:
1332            MethodHandle sum = getReducerFor(pc + 1);
1333            MethodHandle adder = MethodHandles.insertArguments(sum, 0, initial);
1334
1335            // b) Apply lengthers to transform arguments to lengths, producing (<args>)int
1336            adder = MethodHandles.filterArguments(adder, 0, lengthers);
1337
1338            // c) Instantiate StringBuilder (<args>)int -> (<args>)StringBuilder
1339            MethodHandle newBuilder = MethodHandles.filterReturnValue(adder, NEW_STRING_BUILDER);
1340
1341            // d) Fold in StringBuilder constructor, this produces (<args>)StringBuilder
1342            MethodHandle mh = MethodHandles.foldArguments(builder, newBuilder);
1343
1344            // Convert non-primitive arguments to Strings
1345            mh = MethodHandles.filterArguments(mh, 0, filters);
1346
1347            // Convert (<args>)StringBuilder to (<args>)String
1348            if (DEBUG && mode.isExact()) {
1349                mh = MethodHandles.filterReturnValue(mh, BUILDER_TO_STRING_CHECKED);
1350            } else {
1351                mh = MethodHandles.filterReturnValue(mh, BUILDER_TO_STRING);
1352            }
1353
1354            return mh;
1355        }
1356
1357        private static MethodHandle getReducerFor(int cnt) {
1358            return SUMMERS.computeIfAbsent(cnt, SUMMER);
1359        }
1360
1361        private static MethodHandle appender(Class<?> appendType) {
1362            MethodHandle appender = lookupVirtual(MethodHandles.publicLookup(), StringBuilder.class, "append",
1363                    StringBuilder.class, adaptToStringBuilder(appendType));
1364
1365            // appenders should return void, this would not modify the target signature during folding
1366            MethodType nt = MethodType.methodType(void.class, StringBuilder.class, appendType);
1367            return appender.asType(nt);
1368        }
1369
1370        private static String toStringChecked(StringBuilder sb) {
1371            String s = sb.toString();
1372            if (s.length() != sb.capacity()) {
1373                throw new AssertionError("Exactness check failed: result length = " + s.length() + ", buffer capacity = " + sb.capacity());
1374            }
1375            return s;
1376        }
1377
1378        private static int sum(int v1, int v2) {
1379            return v1 + v2;
1380        }
1381
1382        private static int sum(int v1, int v2, int v3) {
1383            return v1 + v2 + v3;
1384        }
1385
1386        private static int sum(int v1, int v2, int v3, int v4) {
1387            return v1 + v2 + v3 + v4;
1388        }
1389
1390        private static int sum(int v1, int v2, int v3, int v4, int v5) {
1391            return v1 + v2 + v3 + v4 + v5;
1392        }
1393
1394        private static int sum(int v1, int v2, int v3, int v4, int v5, int v6) {
1395            return v1 + v2 + v3 + v4 + v5 + v6;
1396        }
1397
1398        private static int sum(int v1, int v2, int v3, int v4, int v5, int v6, int v7) {
1399            return v1 + v2 + v3 + v4 + v5 + v6 + v7;
1400        }
1401
1402        private static int sum(int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8) {
1403            return v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8;
1404        }
1405
1406        private static int sum(int initial, int[] vs) {
1407            int sum = initial;
1408            for (int v : vs) {
1409                sum += v;
1410            }
1411            return sum;
1412        }
1413
1414        private static final ConcurrentMap<Integer, MethodHandle> SUMMERS;
1415
1416        // This one is deliberately non-lambdified to optimize startup time:
1417        private static final Function<Integer, MethodHandle> SUMMER = new Function<Integer, MethodHandle>() {
1418            @Override
1419            public MethodHandle apply(Integer cnt) {
1420                if (cnt == 1) {
1421                    return MethodHandles.identity(int.class);
1422                } else if (cnt <= 8) {
1423                    // Variable-arity collectors are not as efficient as small-count methods,
1424                    // unroll some initial sizes.
1425                    Class<?>[] cls = new Class<?>[cnt];
1426                    Arrays.fill(cls, int.class);
1427                    return lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, cls);
1428                } else {
1429                    return lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, int.class, int[].class)
1430                            .asCollector(int[].class, cnt - 1);
1431                }
1432            }
1433        };
1434
1435        private static final MethodHandle NEW_STRING_BUILDER, STRING_LENGTH, BUILDER_TO_STRING, BUILDER_TO_STRING_CHECKED;
1436
1437        static {
1438            SUMMERS = new ConcurrentHashMap<>();
1439            Lookup publicLookup = MethodHandles.publicLookup();
1440            NEW_STRING_BUILDER = lookupConstructor(publicLookup, StringBuilder.class, int.class);
1441            STRING_LENGTH = lookupVirtual(publicLookup, String.class, "length", int.class);
1442            BUILDER_TO_STRING = lookupVirtual(publicLookup, StringBuilder.class, "toString", String.class);
1443            if (DEBUG) {
1444                BUILDER_TO_STRING_CHECKED = lookupStatic(MethodHandles.Lookup.IMPL_LOOKUP,
1445                        MethodHandleStringBuilderStrategy.class, "toStringChecked", String.class, StringBuilder.class);
1446            } else {
1447                BUILDER_TO_STRING_CHECKED = null;
1448            }
1449        }
1450
1451    }
1452
1453
1454    /**
1455     * <p><b>{@link Strategy#MH_INLINE_SIZED_EXACT}: "MethodHandles inline,
1456     * sized exactly".</b>
1457     *
1458     * <p>This strategy replicates what StringBuilders are doing: it builds the
1459     * byte[] array on its own and passes that byte[] array to String
1460     * constructor. This strategy requires access to some private APIs in JDK,
1461     * most notably, the read-only Integer/Long.stringSize methods that measure
1462     * the character length of the integers, and the private String constructor
1463     * that accepts byte[] arrays without copying. While this strategy assumes a
1464     * particular implementation details for String, this opens the door for
1465     * building a very optimal concatenation sequence. This is the only strategy
1466     * that requires porting if there are private JDK changes occur.
1467     */
1468    private static final class MethodHandleInlineCopyStrategy {
1469        static final Unsafe UNSAFE = Unsafe.getUnsafe();
1470
1471        private MethodHandleInlineCopyStrategy() {
1472            // no instantiation
1473        }
1474
1475        static MethodHandle generate(MethodType mt, Recipe recipe) throws Throwable {
1476
1477            // Create filters and obtain filtered parameter types. Filters would be used in the beginning
1478            // to convert the incoming arguments into the arguments we can process (e.g. Objects -> Strings).
1479            // The filtered argument type list is used all over in the combinators below.
1480            Class<?>[] ptypes = mt.parameterArray();
1481            MethodHandle[] filters = null;
1482            for (int i = 0; i < ptypes.length; i++) {
1483                MethodHandle filter = Stringifiers.forMost(ptypes[i]);
1484                if (filter != null) {
1485                    if (filters == null) {
1486                        filters = new MethodHandle[ptypes.length];
1487                    }
1488                    filters[i] = filter;
1489                    ptypes[i] = filter.type().returnType();
1490                }
1491            }
1492
1493            // Start building the combinator tree. The tree "starts" with (<parameters>)String, and "finishes"
1494            // with the (int, byte[], byte)String in String helper. The combinators are assembled bottom-up,
1495            // which makes the code arguably hard to read.
1496
1497            // Drop all remaining parameter types, leave only helper arguments:
1498            MethodHandle mh;
1499
1500            mh = MethodHandles.dropArguments(NEW_STRING, 3, ptypes);
1501
1502            // Mix in prependers. This happens when (byte[], int, byte) = (storage, index, coder) is already
1503            // known from the combinators below. We are assembling the string backwards, so "index" is the
1504            // *ending* index.
1505            for (RecipeElement el : recipe.getElements()) {
1506                // Do the prepend, and put "new" index at index 1
1507                mh = MethodHandles.dropArguments(mh, 2, int.class);
1508                switch (el.getTag()) {
1509                    case TAG_CONST: {
1510                        MethodHandle prepender = MethodHandles.insertArguments(prepender(String.class), 3, el.getValue());
1511                        mh = MethodHandles.foldArguments(mh, 1, prepender,
1512                                2, 0, 3 // index, storage, coder
1513                        );
1514                        break;
1515                    }
1516                    case TAG_ARG: {
1517                        int pos = el.getArgPos();
1518                        MethodHandle prepender = prepender(ptypes[pos]);
1519                        mh = MethodHandles.foldArguments(mh, 1, prepender,
1520                                2, 0, 3, // index, storage, coder
1521                                4 + pos  // selected argument
1522                        );
1523                        break;
1524                    }
1525                    default:
1526                        throw new StringConcatException("Unhandled tag: " + el.getTag());
1527                }
1528            }
1529
1530            // Fold in byte[] instantiation at argument 0
1531            mh = MethodHandles.foldArguments(mh, 0, NEW_ARRAY,
1532                    1, 2 // index, coder
1533            );
1534
1535            // Start combining length and coder mixers.
1536            //
1537            // Length is easy: constant lengths can be computed on the spot, and all non-constant
1538            // shapes have been either converted to Strings, or explicit methods for getting the
1539            // string length out of primitives are provided.
1540            //
1541            // Coders are more interesting. Only Object, String and char arguments (and constants)
1542            // can have non-Latin1 encoding. It is easier to blindly convert constants to String,
1543            // and deduce the coder from there. Arguments would be either converted to Strings
1544            // during the initial filtering, or handled by primitive specializations in CODER_MIXERS.
1545            //
1546            // The method handle shape after all length and coder mixers is:
1547            //   (int, byte, <args>)String = ("index", "coder", <args>)
1548            byte initialCoder = INITIAL_CODER;
1549            int initialLen = 0;    // initial length, in characters
1550            for (RecipeElement el : recipe.getElements()) {
1551                switch (el.getTag()) {
1552                    case TAG_CONST:
1553                        String constant = el.getValue();
1554                        initialCoder = (byte) coderMixer(String.class).invoke(initialCoder, constant);
1555                        initialLen += constant.length();
1556                        break;
1557                    case TAG_ARG:
1558                        int ac = el.getArgPos();
1559
1560                        Class<?> argClass = ptypes[ac];
1561                        MethodHandle lm = lengthMixer(argClass);
1562                        MethodHandle cm = coderMixer(argClass);
1563
1564                        // Read this bottom up:
1565
1566                        // 4. Drop old index and coder, producing ("new-index", "new-coder", <args>)
1567                        mh = MethodHandles.dropArguments(mh, 2, int.class, byte.class);
1568
1569                        // 3. Compute "new-index", producing ("new-index", "new-coder", "old-index", "old-coder", <args>)
1570                        //    Length mixer needs old index, plus the appropriate argument
1571                        mh = MethodHandles.foldArguments(mh, 0, lm,
1572                                2, // old-index
1573                                4 + ac // selected argument
1574                        );
1575
1576                        // 2. Compute "new-coder", producing ("new-coder", "old-index", "old-coder", <args>)
1577                        //    Coder mixer needs old coder, plus the appropriate argument.
1578                        mh = MethodHandles.foldArguments(mh, 0, cm,
1579                                2, // old-coder
1580                                3 + ac // selected argument
1581                        );
1582
1583                        // 1. The mh shape here is ("old-index", "old-coder", <args>)
1584                        break;
1585                    default:
1586                        throw new StringConcatException("Unhandled tag: " + el.getTag());
1587                }
1588            }
1589
1590            // Insert initial lengths and coders here.
1591            // The method handle shape here is (<args>).
1592            mh = MethodHandles.insertArguments(mh, 0, initialLen, initialCoder);
1593
1594            // Apply filters, converting the arguments:
1595            if (filters != null) {
1596                mh = MethodHandles.filterArguments(mh, 0, filters);
1597            }
1598
1599            return mh;
1600        }
1601
1602        @ForceInline
1603        private static byte[] newArray(int length, byte coder) {
1604            return (byte[]) UNSAFE.allocateUninitializedArray(byte.class, length << coder);
1605        }
1606
1607        private static MethodHandle prepender(Class<?> cl) {
1608            return PREPENDERS.computeIfAbsent(cl, PREPEND);
1609        }
1610
1611        private static MethodHandle coderMixer(Class<?> cl) {
1612            return CODER_MIXERS.computeIfAbsent(cl, CODER_MIX);
1613        }
1614
1615        private static MethodHandle lengthMixer(Class<?> cl) {
1616            return LENGTH_MIXERS.computeIfAbsent(cl, LENGTH_MIX);
1617        }
1618
1619        // This one is deliberately non-lambdified to optimize startup time:
1620        private static final Function<Class<?>, MethodHandle> PREPEND = new Function<Class<?>, MethodHandle>() {
1621            @Override
1622            public MethodHandle apply(Class<?> c) {
1623                return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "prepend", int.class, int.class, byte[].class, byte.class,
1624                        Wrapper.asPrimitiveType(c));
1625            }
1626        };
1627
1628        // This one is deliberately non-lambdified to optimize startup time:
1629        private static final Function<Class<?>, MethodHandle> CODER_MIX = new Function<Class<?>, MethodHandle>() {
1630            @Override
1631            public MethodHandle apply(Class<?> c) {
1632                return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mixCoder", byte.class, byte.class,
1633                        Wrapper.asPrimitiveType(c));
1634            }
1635        };
1636
1637        // This one is deliberately non-lambdified to optimize startup time:
1638        private static final Function<Class<?>, MethodHandle> LENGTH_MIX = new Function<Class<?>, MethodHandle>() {
1639            @Override
1640            public MethodHandle apply(Class<?> c) {
1641                return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mixLen", int.class, int.class,
1642                        Wrapper.asPrimitiveType(c));
1643            }
1644        };
1645
1646        private static final MethodHandle NEW_STRING;
1647        private static final MethodHandle NEW_ARRAY;
1648        private static final ConcurrentMap<Class<?>, MethodHandle> PREPENDERS;
1649        private static final ConcurrentMap<Class<?>, MethodHandle> LENGTH_MIXERS;
1650        private static final ConcurrentMap<Class<?>, MethodHandle> CODER_MIXERS;
1651        private static final byte INITIAL_CODER;
1652        static final Class<?> STRING_HELPER;
1653
1654        static {
1655            try {
1656                STRING_HELPER = Class.forName("java.lang.StringConcatHelper");
1657                MethodHandle initCoder = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "initialCoder", byte.class);
1658                INITIAL_CODER = (byte) initCoder.invoke();
1659            } catch (Throwable e) {
1660                throw new AssertionError(e);
1661            }
1662
1663            PREPENDERS = new ConcurrentHashMap<>();
1664            LENGTH_MIXERS = new ConcurrentHashMap<>();
1665            CODER_MIXERS = new ConcurrentHashMap<>();
1666
1667            NEW_STRING = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newString", String.class, byte[].class, int.class, byte.class);
1668            NEW_ARRAY  = lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleInlineCopyStrategy.class, "newArray", byte[].class, int.class, byte.class);
1669        }
1670    }
1671
1672    /**
1673     * Public gateways to public "stringify" methods. These methods have the form String apply(T obj), and normally
1674     * delegate to {@code String.valueOf}, depending on argument's type.
1675     */
1676    private static final class Stringifiers {
1677        private Stringifiers() {
1678            // no instantiation
1679        }
1680
1681        private static class StringifierMost extends ClassValue<MethodHandle> {
1682            @Override
1683            protected MethodHandle computeValue(Class<?> cl) {
1684                if (cl == String.class) {
1685                    return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, Object.class);
1686                } else if (cl == float.class) {
1687                    return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, float.class);
1688                } else if (cl == double.class) {
1689                    return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, double.class);
1690                } else if (!cl.isPrimitive()) {
1691                    MethodHandle mhObject = lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, Object.class);
1692
1693                    // We need the additional conversion here, because String.valueOf(Object) may return null.
1694                    // String conversion rules in Java state we need to produce "null" String in this case.
1695                    // It can be easily done with applying valueOf the second time.
1696                    return MethodHandles.filterReturnValue(mhObject,
1697                            mhObject.asType(MethodType.methodType(String.class, String.class)));
1698                }
1699
1700                return null;
1701            }
1702        }
1703
1704        private static class StringifierAny extends ClassValue<MethodHandle> {
1705            @Override
1706            protected MethodHandle computeValue(Class<?> cl) {
1707                if (cl == byte.class || cl == short.class || cl == int.class) {
1708                    return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, int.class);
1709                } else if (cl == boolean.class) {
1710                    return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, boolean.class);
1711                } else if (cl == char.class) {
1712                    return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, char.class);
1713                } else if (cl == long.class) {
1714                    return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, long.class);
1715                } else {
1716                    MethodHandle mh = STRINGIFIERS_MOST.get(cl);
1717                    if (mh != null) {
1718                        return mh;
1719                    } else {
1720                        throw new IllegalStateException("Unknown class: " + cl);
1721                    }
1722                }
1723            }
1724        }
1725
1726        private static final ClassValue<MethodHandle> STRINGIFIERS_MOST = new StringifierMost();
1727        private static final ClassValue<MethodHandle> STRINGIFIERS_ANY = new StringifierAny();
1728
1729        /**
1730         * Returns a stringifier for references and floats/doubles only.
1731         * Always returns null for other primitives.
1732         *
1733         * @param t class to stringify
1734         * @return stringifier; null, if not available
1735         */
1736        static MethodHandle forMost(Class<?> t) {
1737            return STRINGIFIERS_MOST.get(t);
1738        }
1739
1740        /**
1741         * Returns a stringifier for any type. Never returns null.
1742         *
1743         * @param t class to stringify
1744         * @return stringifier
1745         */
1746        static MethodHandle forAny(Class<?> t) {
1747            return STRINGIFIERS_ANY.get(t);
1748        }
1749    }
1750
1751    /* ------------------------------- Common utilities ------------------------------------ */
1752
1753    static MethodHandle lookupStatic(Lookup lookup, Class<?> refc, String name, Class<?> rtype, Class<?>... ptypes) {
1754        try {
1755            return lookup.findStatic(refc, name, MethodType.methodType(rtype, ptypes));
1756        } catch (NoSuchMethodException | IllegalAccessException e) {
1757            throw new AssertionError(e);
1758        }
1759    }
1760
1761    static MethodHandle lookupVirtual(Lookup lookup, Class<?> refc, String name, Class<?> rtype, Class<?>... ptypes) {
1762        try {
1763            return lookup.findVirtual(refc, name, MethodType.methodType(rtype, ptypes));
1764        } catch (NoSuchMethodException | IllegalAccessException e) {
1765            throw new AssertionError(e);
1766        }
1767    }
1768
1769    static MethodHandle lookupConstructor(Lookup lookup, Class<?> refc, Class<?> ptypes) {
1770        try {
1771            return lookup.findConstructor(refc, MethodType.methodType(void.class, ptypes));
1772        } catch (NoSuchMethodException | IllegalAccessException e) {
1773            throw new AssertionError(e);
1774        }
1775    }
1776
1777    static int estimateSize(Class<?> cl) {
1778        if (cl == Integer.TYPE) {
1779            return 11; // "-2147483648"
1780        } else if (cl == Boolean.TYPE) {
1781            return 5; // "false"
1782        } else if (cl == Byte.TYPE) {
1783            return 4; // "-128"
1784        } else if (cl == Character.TYPE) {
1785            return 1; // duh
1786        } else if (cl == Short.TYPE) {
1787            return 6; // "-32768"
1788        } else if (cl == Double.TYPE) {
1789            return 26; // apparently, no larger than this, see FloatingDecimal.BinaryToASCIIBuffer.buffer
1790        } else if (cl == Float.TYPE) {
1791            return 26; // apparently, no larger than this, see FloatingDecimal.BinaryToASCIIBuffer.buffer
1792        } else if (cl == Long.TYPE)  {
1793            return 20; // "-9223372036854775808"
1794        } else {
1795            throw new IllegalArgumentException("Cannot estimate the size for " + cl);
1796        }
1797    }
1798
1799    static Class<?> adaptToStringBuilder(Class<?> c) {
1800        if (c.isPrimitive()) {
1801            if (c == Byte.TYPE || c == Short.TYPE) {
1802                return int.class;
1803            }
1804        } else {
1805            if (c != String.class) {
1806                return Object.class;
1807            }
1808        }
1809        return c;
1810    }
1811
1812    private StringConcatFactory() {
1813        // no instantiation
1814    }
1815
1816}
1817