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