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