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