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