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