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 jdk.internal.org.objectweb.asm.Opcodes.*;
  48 
  49 /**
  50  * <p>Methods to facilitate the creation of String concatenation methods, that
  51  * can be used to efficiently concatenate a known number of arguments of known
  52  * types, possibly after type adaptation and partial evaluation of arguments.
  53  * These methods are typically used as <em>bootstrap methods</em> for {@code
  54  * invokedynamic} call sites, to support the <em>string concatenation</em>
  55  * feature of the Java Programming Language.
  56  *
  57  * <p>Indirect access to the behavior specified by the provided {@code
  58  * MethodHandle} proceeds in order through two phases:
  59  *
  60  * <ol>
  61  *     <li><em>Linkage</em> occurs when the methods in this class are invoked.
  62  * They take as arguments a method type describing the concatenated arguments
  63  * count and types, and optionally the String <em>recipe</em>, plus the
  64  * constants that participate in the String concatenation. The details on
  65  * accepted recipe shapes are described further below. Linkage may involve
  66  * dynamically loading a new class that implements the expected concatenation
  67  * behavior. The {@code CallSite} holds the {@code MethodHandle} pointing to the
  68  * exact concatenation method. The concatenation methods may be shared among
  69  * different {@code CallSite}s, e.g. if linkage methods produce them as pure
  70  * functions.</li>
  71  *
  72  * <li><em>Invocation</em> occurs when a generated concatenation method is
  73  * invoked with the exact dynamic arguments. This may occur many times for a
  74  * single concatenation method. The method referenced by the behavior {@code
  75  * MethodHandle} is invoked with the static arguments and any additional dynamic
  76  * arguments provided on invocation, as if by {@link MethodHandle#invoke(Object...)}.</li>
  77  * </ol>
  78  *
  79  * <p> This class provides two forms of linkage methods: a simple version
  80  * ({@link #makeConcat(java.lang.invoke.MethodHandles.Lookup, String,
  81  * MethodType)}) using only the dynamic arguments, and an advanced version
  82  * ({@link #makeConcatWithConstants(java.lang.invoke.MethodHandles.Lookup,
  83  * String, MethodType, String, Object...)} using the advanced forms of capturing
  84  * the constant arguments. The advanced strategy can produce marginally better
  85  * invocation bytecode, at the expense of exploding the number of shapes of
  86  * string concatenation methods present at runtime, because those shapes would
  87  * include constant static arguments as well.
  88  *
  89  * @author Aleksey Shipilev
  90  * @author Remi Forax
  91  * @author Peter Levart
  92  *
  93  * @apiNote
  94  * <p>There is a JVM limit (classfile structural constraint): no method
  95  * can call with more than 255 slots. This limits the number of static and
  96  * dynamic arguments one can pass to bootstrap method. Since there are potential
  97  * concatenation strategies that use {@code MethodHandle} combinators, we need
  98  * to reserve a few empty slots on the parameter lists to capture the
  99  * temporal results. This is why bootstrap methods in this factory do not accept
 100  * more than 200 argument slots. Users requiring more than 200 argument slots in
 101  * concatenation are expected to split the large concatenation in smaller
 102  * expressions.
 103  *
 104  * @since 9
 105  */
 106 public final class StringConcatFactory {
 107 
 108     /**
 109      * Tag used to demarcate an ordinary argument.
 110      */
 111     private static final char TAG_ARG = '\u0001';
 112 
 113     /**
 114      * Tag used to demarcate a constant.
 115      */
 116     private static final char TAG_CONST = '\u0002';
 117 
 118     /**
 119      * Maximum number of argument slots in String Concat call.
 120      *
 121      * While the maximum number of argument slots that indy call can handle is 253,
 122      * we do not use all those slots, to let the strategies with MethodHandle
 123      * combinators to use some arguments.
 124      */
 125     private static final int MAX_INDY_CONCAT_ARG_SLOTS = 200;
 126 
 127     /**
 128      * Concatenation strategy to use. See {@link Strategy} for possible options.
 129      * This option is controllable with -Djava.lang.invoke.stringConcat JDK option.
 130      */
 131     private static Strategy STRATEGY;
 132 
 133     /**
 134      * Default strategy to use for concatenation.
 135      */
 136     private static final Strategy DEFAULT_STRATEGY = Strategy.MH_INLINE_SIZED_EXACT;
 137 
 138     private enum Strategy {
 139         /**
 140          * Bytecode generator, calling into {@link java.lang.StringBuilder}.
 141          */
 142         BC_SB,
 143 
 144         /**
 145          * Bytecode generator, calling into {@link java.lang.StringBuilder};
 146          * but trying to estimate the required storage.
 147          */
 148         BC_SB_SIZED,
 149 
 150         /**
 151          * Bytecode generator, calling into {@link java.lang.StringBuilder};
 152          * but computing the required storage exactly.
 153          */
 154         BC_SB_SIZED_EXACT,
 155 
 156         /**
 157          * MethodHandle-based generator, that in the end calls into {@link java.lang.StringBuilder}.
 158          * This strategy also tries to estimate the required storage.
 159          */
 160         MH_SB_SIZED,
 161 
 162         /**
 163          * MethodHandle-based generator, that in the end calls into {@link java.lang.StringBuilder}.
 164          * This strategy also estimate the required storage exactly.
 165          */
 166         MH_SB_SIZED_EXACT,
 167 
 168         /**
 169          * MethodHandle-based generator, that constructs its own byte[] array from
 170          * the arguments. It computes the required storage exactly.
 171          */
 172         MH_INLINE_SIZED_EXACT
 173     }
 174 
 175     /**
 176      * Enables debugging: this may print debugging messages, perform additional (non-neutral for performance)
 177      * checks, etc.
 178      */
 179     private static final boolean DEBUG;
 180 
 181     /**
 182      * Enables caching of strategy stubs. This may improve the linkage time by reusing the generated
 183      * code, at the expense of contaminating the profiles.
 184      */
 185     private static final boolean CACHE_ENABLE;
 186 
 187     private static final ConcurrentMap<Key, MethodHandle> CACHE;
 188 
 189     /**
 190      * Dump generated classes to disk, for debugging purposes.
 191      */
 192     private static final ProxyClassesDumper DUMPER;
 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         Properties props = GetPropertyAction.privilegedGetProperties();
 206         final String strategy =
 207                 props.getProperty("java.lang.invoke.stringConcat");
 208         CACHE_ENABLE = Boolean.parseBoolean(
 209                 props.getProperty("java.lang.invoke.stringConcat.cache"));
 210         DEBUG = Boolean.parseBoolean(
 211                 props.getProperty("java.lang.invoke.stringConcat.debug"));
 212         final String dumpPath =
 213                 props.getProperty("java.lang.invoke.stringConcat.dumpClasses");
 214 
 215         STRATEGY = (strategy == null) ? DEFAULT_STRATEGY : Strategy.valueOf(strategy);
 216         CACHE = CACHE_ENABLE ? new ConcurrentHashMap<>() : null;
 217         DUMPER = (dumpPath == null) ? null : ProxyClassesDumper.getInstance(dumpPath);
 218     }
 219 
 220     /**
 221      * Cache key is a composite of:
 222      *   - class name, that lets to disambiguate stubs, to avoid excess sharing
 223      *   - method type, describing the dynamic arguments for concatenation
 224      *   - concat recipe, describing the constants and concat shape
 225      */
 226     private static final class Key {
 227         final String className;
 228         final MethodType mt;
 229         final Recipe recipe;
 230 
 231         public Key(String className, MethodType mt, Recipe recipe) {
 232             this.className = className;
 233             this.mt = mt;
 234             this.recipe = recipe;
 235         }
 236 
 237         @Override
 238         public boolean equals(Object o) {
 239             if (this == o) return true;
 240             if (o == null || getClass() != o.getClass()) return false;
 241 
 242             Key key = (Key) o;
 243 
 244             if (!className.equals(key.className)) return false;
 245             if (!mt.equals(key.mt)) return false;
 246             if (!recipe.equals(key.recipe)) return false;
 247             return true;
 248         }
 249 
 250         @Override
 251         public int hashCode() {
 252             int result = className.hashCode();
 253             result = 31 * result + mt.hashCode();
 254             result = 31 * result + recipe.hashCode();
 255             return result;
 256         }
 257     }
 258 
 259     /**
 260      * Parses the recipe string, and produces the traversable collection of
 261      * {@link java.lang.invoke.StringConcatFactory.RecipeElement}-s for generator
 262      * strategies. Notably, this class parses out the constants from the recipe
 263      * and from other static arguments.
 264      */
 265     private static final class Recipe {
 266         private final List<RecipeElement> elements;
 267 
 268         public Recipe(String src, Object[] constants) {
 269             List<RecipeElement> el = new ArrayList<>();
 270 
 271             int constC = 0;
 272             int argC = 0;
 273 
 274             StringBuilder acc = new StringBuilder();
 275 
 276             for (int i = 0; i < src.length(); i++) {
 277                 char c = src.charAt(i);
 278 
 279                 if (c == TAG_CONST || c == TAG_ARG) {
 280                     // Detected a special tag, flush all accumulated characters
 281                     // as a constant first:
 282                     if (acc.length() > 0) {
 283                         el.add(new RecipeElement(acc.toString()));
 284                         acc.setLength(0);
 285                     }
 286                     if (c == TAG_CONST) {
 287                         Object cnst = constants[constC++];
 288                         el.add(new RecipeElement(cnst));
 289                     } else if (c == TAG_ARG) {
 290                         el.add(new RecipeElement(argC++));
 291                     }
 292                 } else {
 293                     // Not a special character, this is a constant embedded into
 294                     // the recipe itself.
 295                     acc.append(c);
 296                 }
 297             }
 298 
 299             // Flush the remaining characters as constant:
 300             if (acc.length() > 0) {
 301                 el.add(new RecipeElement(acc.toString()));
 302             }
 303 
 304             elements = el;
 305         }
 306 
 307         public List<RecipeElement> getElements() {
 308             return elements;
 309         }
 310 
 311         @Override
 312         public boolean equals(Object o) {
 313             if (this == o) return true;
 314             if (o == null || getClass() != o.getClass()) return false;
 315 
 316             Recipe recipe = (Recipe) o;
 317             return elements.equals(recipe.elements);
 318         }
 319 
 320         @Override
 321         public String toString() {
 322             return "Recipe{" +
 323                     "elements=" + elements +
 324                     '}';
 325         }
 326 
 327         @Override
 328         public int hashCode() {
 329             return elements.hashCode();
 330         }
 331     }
 332 
 333     private static final class RecipeElement {
 334         private final String value;
 335         private final int argPos;
 336         private final char tag;
 337 
 338         public RecipeElement(Object cnst) {
 339             this.value = String.valueOf(Objects.requireNonNull(cnst));
 340             this.argPos = -1;
 341             this.tag = TAG_CONST;
 342         }
 343 
 344         public RecipeElement(int arg) {
 345             this.value = null;
 346             this.argPos = arg;
 347             this.tag = TAG_ARG;
 348         }
 349 
 350         public String getValue() {
 351             assert (tag == TAG_CONST);
 352             return value;
 353         }
 354 
 355         public int getArgPos() {
 356             assert (tag == TAG_ARG);
 357             return argPos;
 358         }
 359 
 360         public char getTag() {
 361             return tag;
 362         }
 363 
 364         @Override
 365         public boolean equals(Object o) {
 366             if (this == o) return true;
 367             if (o == null || getClass() != o.getClass()) return false;
 368 
 369             RecipeElement that = (RecipeElement) o;
 370 
 371             if (this.tag != that.tag) return false;
 372             if (this.tag == TAG_CONST && (!value.equals(that.value))) return false;
 373             if (this.tag == TAG_ARG && (argPos != that.argPos)) return false;
 374             return true;
 375         }
 376 
 377         @Override
 378         public String toString() {
 379             return "RecipeElement{" +
 380                     "value='" + value + '\'' +
 381                     ", argPos=" + argPos +
 382                     ", tag=" + tag +
 383                     '}';
 384         }
 385 
 386         @Override
 387         public int hashCode() {
 388             return (int)tag;
 389         }
 390     }
 391 
 392     // StringConcatFactory bootstrap methods are startup sensitive, and may be
 393     // special cased in java.lang.invokeBootstrapMethodInvoker to ensure
 394     // methods are invoked with exact type information to avoid generating
 395     // code for runtime checks. Take care any changes or additions here are
 396     // reflected there as appropriate.
 397 
 398     /**
 399      * Facilitates the creation of optimized String concatenation methods, that
 400      * can be used to efficiently concatenate a known number of arguments of
 401      * known types, possibly after type adaptation and partial evaluation of
 402      * arguments. Typically used as a <em>bootstrap method</em> for {@code
 403      * invokedynamic} call sites, to support the <em>string concatenation</em>
 404      * feature of the Java Programming Language.
 405      *
 406      * <p>When the target of the {@code CallSite} returned from this method is
 407      * invoked, it returns the result of String concatenation, taking all
 408      * function arguments passed to the linkage method as inputs for
 409      * concatenation. The target signature is given by {@code concatType}.
 410      * For a target accepting:
 411      * <ul>
 412      *     <li>zero inputs, concatenation results in an empty string;</li>
 413      *     <li>one input, concatenation results in the single
 414      *     input converted as per JLS 5.1.11 "String Conversion"; otherwise</li>
 415      *     <li>two or more inputs, the inputs are concatenated as per
 416      *     requirements stated in JLS 15.18.1 "String Concatenation Operator +".
 417      *     The inputs are converted as per JLS 5.1.11 "String Conversion",
 418      *     and combined from left to right.</li>
 419      * </ul>
 420      *
 421      * <p>Assume the linkage arguments are as follows:
 422      *
 423      * <ul>
 424      *     <li>{@code concatType}, describing the {@code CallSite} signature</li>
 425      * </ul>
 426      *
 427      * <p>Then the following linkage invariants must hold:
 428      *
 429      * <ul>
 430      *     <li>The number of parameter slots in {@code concatType} is
 431      *         less than or equal to 200</li>
 432      *     <li>The return type in {@code concatType} is assignable from {@link java.lang.String}</li>
 433      * </ul>
 434      *
 435      * @param lookup   Represents a lookup context with the accessibility
 436      *                 privileges of the caller. Specifically, the lookup
 437      *                 context must have
 438      *                 <a href="MethodHandles.Lookup.html#privacc">private access</a>
 439      *                 privileges.
 440      *                 When used with {@code invokedynamic}, this is stacked
 441      *                 automatically by the VM.
 442      * @param name     The name of the method to implement. This name is
 443      *                 arbitrary, and has no meaning for this linkage method.
 444      *                 When used with {@code invokedynamic}, this is provided by
 445      *                 the {@code NameAndType} of the {@code InvokeDynamic}
 446      *                 structure and is stacked automatically by the VM.
 447      * @param concatType The expected signature of the {@code CallSite}.  The
 448      *                   parameter types represent the types of concatenation
 449      *                   arguments; the return type is always assignable from {@link
 450      *                   java.lang.String}.  When used with {@code invokedynamic},
 451      *                   this is provided by the {@code NameAndType} of the {@code
 452      *                   InvokeDynamic} structure and is stacked automatically by
 453      *                   the VM.
 454      * @return a CallSite whose target can be used to perform String
 455      * concatenation, with dynamic concatenation arguments described by the given
 456      * {@code concatType}.
 457      * @throws StringConcatException If any of the linkage invariants described
 458      *                               here are violated, or the lookup context
 459      *                               does not have private access privileges.
 460      * @throws NullPointerException If any of the incoming arguments is null.
 461      *                              This will never happen when a bootstrap method
 462      *                              is called with invokedynamic.
 463      *
 464      * @jls  5.1.11 String Conversion
 465      * @jls 15.18.1 String Concatenation Operator +
 466      */
 467     public static CallSite makeConcat(MethodHandles.Lookup lookup,
 468                                       String name,
 469                                       MethodType concatType) throws StringConcatException {
 470         if (DEBUG) {
 471             System.out.println("StringConcatFactory " + STRATEGY + " is here for " + concatType);
 472         }
 473 
 474         return doStringConcat(lookup, name, concatType, true, null);
 475     }
 476 
 477     /**
 478      * Facilitates the creation of optimized String concatenation methods, that
 479      * can be used to efficiently concatenate a known number of arguments of
 480      * known types, possibly after type adaptation and partial evaluation of
 481      * arguments. Typically used as a <em>bootstrap method</em> for {@code
 482      * invokedynamic} call sites, to support the <em>string concatenation</em>
 483      * feature of the Java Programming Language.
 484      *
 485      * <p>When the target of the {@code CallSite} returned from this method is
 486      * invoked, it returns the result of String concatenation, taking all
 487      * function arguments and constants passed to the linkage method as inputs for
 488      * concatenation. The target signature is given by {@code concatType}, and
 489      * does not include constants.
 490      * For a target accepting:
 491      * <ul>
 492      *     <li>zero inputs, concatenation results in an empty string;</li>
 493      *     <li>one input, concatenation results in the single
 494      *     input converted as per JLS 5.1.11 "String Conversion"; otherwise</li>
 495      *     <li>two or more inputs, the inputs are concatenated as per
 496      *     requirements stated in JLS 15.18.1 "String Concatenation Operator +".
 497      *     The inputs are converted as per JLS 5.1.11 "String Conversion",
 498      *     and combined from left to right.</li>
 499      * </ul>
 500      *
 501      * <p>The concatenation <em>recipe</em> is a String description for the way to
 502      * construct a concatenated String from the arguments and constants. The
 503      * recipe is processed from left to right, and each character represents an
 504      * input to concatenation. Recipe characters mean:
 505      *
 506      * <ul>
 507      *
 508      *   <li><em>\1 (Unicode point 0001)</em>: an ordinary argument. This
 509      *   input is passed through dynamic argument, and is provided during the
 510      *   concatenation method invocation. This input can be null.</li>
 511      *
 512      *   <li><em>\2 (Unicode point 0002):</em> a constant. This input passed
 513      *   through static bootstrap argument. This constant can be any value
 514      *   representable in constant pool. If necessary, the factory would call
 515      *   {@code toString} to perform a one-time String conversion.</li>
 516      *
 517      *   <li><em>Any other char value:</em> a single character constant.</li>
 518      * </ul>
 519      *
 520      * <p>Assume the linkage arguments are as follows:
 521      *
 522      * <ul>
 523      *   <li>{@code concatType}, describing the {@code CallSite} signature</li>
 524      *   <li>{@code recipe}, describing the String recipe</li>
 525      *   <li>{@code constants}, the vararg array of constants</li>
 526      * </ul>
 527      *
 528      * <p>Then the following linkage invariants must hold:
 529      *
 530      * <ul>
 531      *   <li>The number of parameter slots in {@code concatType} is less than
 532      *       or equal to 200</li>
 533      *
 534      *   <li>The parameter count in {@code concatType} equals to number of \1 tags
 535      *   in {@code recipe}</li>
 536      *
 537      *   <li>The return type in {@code concatType} is assignable
 538      *   from {@link java.lang.String}, and matches the return type of the
 539      *   returned {@link MethodHandle}</li>
 540      *
 541      *   <li>The number of elements in {@code constants} equals to number of \2
 542      *   tags in {@code recipe}</li>
 543      * </ul>
 544      *
 545      * @param lookup    Represents a lookup context with the accessibility
 546      *                  privileges of the caller. Specifically, the lookup
 547      *                  context must have
 548      *                  <a href="MethodHandles.Lookup.html#privacc">private access</a>
 549      *                  privileges.
 550      *                  When used with {@code invokedynamic}, this is stacked
 551      *                  automatically by the VM.
 552      * @param name      The name of the method to implement. This name is
 553      *                  arbitrary, and has no meaning for this linkage method.
 554      *                  When used with {@code invokedynamic}, this is provided
 555      *                  by the {@code NameAndType} of the {@code InvokeDynamic}
 556      *                  structure and is stacked automatically by the VM.
 557      * @param concatType The expected signature of the {@code CallSite}.  The
 558      *                  parameter types represent the types of dynamic concatenation
 559      *                  arguments; the return type is always assignable from {@link
 560      *                  java.lang.String}.  When used with {@code
 561      *                  invokedynamic}, this is provided by the {@code
 562      *                  NameAndType} of the {@code InvokeDynamic} structure and
 563      *                  is stacked automatically by the VM.
 564      * @param recipe    Concatenation recipe, described above.
 565      * @param constants A vararg parameter representing the constants passed to
 566      *                  the linkage method.
 567      * @return a CallSite whose target can be used to perform String
 568      * concatenation, with dynamic concatenation arguments described by the given
 569      * {@code concatType}.
 570      * @throws StringConcatException If any of the linkage invariants described
 571      *                               here are violated, or the lookup context
 572      *                               does not have private access privileges.
 573      * @throws NullPointerException If any of the incoming arguments is null, or
 574      *                              any constant in {@code recipe} is null.
 575      *                              This will never happen when a bootstrap method
 576      *                              is called with invokedynamic.
 577      * @apiNote Code generators have three distinct ways to process a constant
 578      * string operand S in a string concatenation expression.  First, S can be
 579      * materialized as a reference (using ldc) and passed as an ordinary argument
 580      * (recipe '\1'). Or, S can be stored in the constant pool and passed as a
 581      * constant (recipe '\2') . Finally, if S contains neither of the recipe
 582      * tag characters ('\1', '\2') then S can be interpolated into the recipe
 583      * itself, causing its characters to be inserted into the result.
 584      *
 585      * @jls  5.1.11 String Conversion
 586      * @jls 15.18.1 String Concatenation Operator +
 587      */
 588     public static CallSite makeConcatWithConstants(MethodHandles.Lookup lookup,
 589                                                    String name,
 590                                                    MethodType concatType,
 591                                                    String recipe,
 592                                                    Object... constants) throws StringConcatException {
 593         if (DEBUG) {
 594             System.out.println("StringConcatFactory " + STRATEGY + " is here for " + concatType + ", {" + recipe + "}, " + Arrays.toString(constants));
 595         }
 596 
 597         return doStringConcat(lookup, name, concatType, false, recipe, constants);
 598     }
 599 
 600     private static CallSite doStringConcat(MethodHandles.Lookup lookup,
 601                                            String name,
 602                                            MethodType concatType,
 603                                            boolean generateRecipe,
 604                                            String recipe,
 605                                            Object... constants) throws StringConcatException {
 606         Objects.requireNonNull(lookup, "Lookup is null");
 607         Objects.requireNonNull(name, "Name is null");
 608         Objects.requireNonNull(concatType, "Concat type is null");
 609         Objects.requireNonNull(constants, "Constants are null");
 610 
 611         for (Object o : constants) {
 612             Objects.requireNonNull(o, "Cannot accept null constants");
 613         }
 614 
 615         if ((lookup.lookupModes() & MethodHandles.Lookup.PRIVATE) == 0) {
 616             throw new StringConcatException("Invalid caller: " +
 617                     lookup.lookupClass().getName());
 618         }
 619 
 620         int cCount = 0;
 621         int oCount = 0;
 622         if (generateRecipe) {
 623             // Mock the recipe to reuse the concat generator code
 624             char[] value = new char[concatType.parameterCount()];
 625             Arrays.fill(value, TAG_ARG);
 626             recipe = new String(value);
 627             oCount = concatType.parameterCount();
 628         } else {
 629             Objects.requireNonNull(recipe, "Recipe is null");
 630 
 631             for (int i = 0; i < recipe.length(); i++) {
 632                 char c = recipe.charAt(i);
 633                 if (c == TAG_CONST) cCount++;
 634                 if (c == TAG_ARG)   oCount++;
 635             }
 636         }
 637 
 638         if (oCount != concatType.parameterCount()) {
 639             throw new StringConcatException(
 640                     "Mismatched number of concat arguments: recipe wants " +
 641                             oCount +
 642                             " arguments, but signature provides " +
 643                             concatType.parameterCount());
 644         }
 645 
 646         if (cCount != constants.length) {
 647             throw new StringConcatException(
 648                     "Mismatched number of concat constants: recipe wants " +
 649                             cCount +
 650                             " constants, but only " +
 651                             constants.length +
 652                             " are passed");
 653         }
 654 
 655         if (!concatType.returnType().isAssignableFrom(String.class)) {
 656             throw new StringConcatException(
 657                     "The return type should be compatible with String, but it is " +
 658                             concatType.returnType());
 659         }
 660 
 661         if (concatType.parameterSlotCount() > MAX_INDY_CONCAT_ARG_SLOTS) {
 662             throw new StringConcatException("Too many concat argument slots: " +
 663                     concatType.parameterSlotCount() +
 664                     ", can only accept " +
 665                     MAX_INDY_CONCAT_ARG_SLOTS);
 666         }
 667 
 668         String className = getClassName(lookup.lookupClass());
 669         MethodType mt = adaptType(concatType);
 670         Recipe rec = new Recipe(recipe, constants);
 671 
 672         MethodHandle mh;
 673         if (CACHE_ENABLE) {
 674             Key key = new Key(className, mt, rec);
 675             mh = CACHE.get(key);
 676             if (mh == null) {
 677                 mh = generate(lookup, className, mt, rec);
 678                 CACHE.put(key, mh);
 679             }
 680         } else {
 681             mh = generate(lookup, className, mt, rec);
 682         }
 683         return new ConstantCallSite(mh.asType(concatType));
 684     }
 685 
 686     /**
 687      * Adapt method type to an API we are going to use.
 688      *
 689      * This strips the concrete classes from the signatures, thus preventing
 690      * class leakage when we cache the concatenation stubs.
 691      *
 692      * @param args actual argument types
 693      * @return argument types the strategy is going to use
 694      */
 695     private static MethodType adaptType(MethodType args) {
 696         Class<?>[] ptypes = null;
 697         for (int i = 0; i < args.parameterCount(); i++) {
 698             Class<?> ptype = args.parameterType(i);
 699             if (!ptype.isPrimitive() &&
 700                     ptype != String.class &&
 701                     ptype != Object.class) { // truncate to Object
 702                 if (ptypes == null) {
 703                     ptypes = args.parameterArray();
 704                 }
 705                 ptypes[i] = Object.class;
 706             }
 707             // else other primitives or String or Object (unchanged)
 708         }
 709         return (ptypes != null)
 710                 ? MethodType.methodType(args.returnType(), ptypes)
 711                 : args;
 712     }
 713 
 714     private static String getClassName(Class<?> hostClass) throws StringConcatException {
 715         /*
 716           When cache is enabled, we want to cache as much as we can.
 717 
 718           However, there are two peculiarities:
 719 
 720            a) The generated class should stay within the same package as the
 721               host class, to allow Unsafe.defineAnonymousClass access controls
 722               to work properly. JDK may choose to fail with IllegalAccessException
 723               when accessing a VM anonymous class with non-privileged callers,
 724               see JDK-8058575.
 725 
 726            b) If we mark the stub with some prefix, say, derived from the package
 727               name because of (a), we can technically use that stub in other packages.
 728               But the call stack traces would be extremely puzzling to unsuspecting users
 729               and profiling tools: whatever stub wins the race, would be linked in all
 730               similar callsites.
 731 
 732            Therefore, we set the class prefix to match the host class package, and use
 733            the prefix as the cache key too. This only affects BC_* strategies, and only when
 734            cache is enabled.
 735          */
 736 
 737         switch (STRATEGY) {
 738             case BC_SB:
 739             case BC_SB_SIZED:
 740             case BC_SB_SIZED_EXACT: {
 741                 if (CACHE_ENABLE) {
 742                     String pkgName = hostClass.getPackageName();
 743                     return (pkgName != null && !pkgName.isEmpty() ? pkgName.replace('.', '/') + "/" : "") + "Stubs$$StringConcat";
 744                 } else {
 745                     return hostClass.getName().replace('.', '/') + "$$StringConcat";
 746                 }
 747             }
 748             case MH_SB_SIZED:
 749             case MH_SB_SIZED_EXACT:
 750             case MH_INLINE_SIZED_EXACT:
 751                 // MethodHandle strategies do not need a class name.
 752                 return "";
 753             default:
 754                 throw new StringConcatException("Concatenation strategy " + STRATEGY + " is not implemented");
 755         }
 756     }
 757 
 758     private static MethodHandle generate(Lookup lookup, String className, MethodType mt, Recipe recipe) throws StringConcatException {
 759         try {
 760             switch (STRATEGY) {
 761                 case BC_SB:
 762                     return BytecodeStringBuilderStrategy.generate(lookup, className, mt, recipe, Mode.DEFAULT);
 763                 case BC_SB_SIZED:
 764                     return BytecodeStringBuilderStrategy.generate(lookup, className, mt, recipe, Mode.SIZED);
 765                 case BC_SB_SIZED_EXACT:
 766                     return BytecodeStringBuilderStrategy.generate(lookup, className, mt, recipe, Mode.SIZED_EXACT);
 767                 case MH_SB_SIZED:
 768                     return MethodHandleStringBuilderStrategy.generate(mt, recipe, Mode.SIZED);
 769                 case MH_SB_SIZED_EXACT:
 770                     return MethodHandleStringBuilderStrategy.generate(mt, recipe, Mode.SIZED_EXACT);
 771                 case MH_INLINE_SIZED_EXACT:
 772                     return MethodHandleInlineCopyStrategy.generate(mt, recipe);
 773                 default:
 774                     throw new StringConcatException("Concatenation strategy " + STRATEGY + " is not implemented");
 775             }
 776         } catch (Error | StringConcatException e) {
 777             // Pass through any error or existing StringConcatException
 778             throw e;
 779         } catch (Throwable t) {
 780             throw new StringConcatException("Generator failed", t);
 781         }
 782     }
 783 
 784     private enum Mode {
 785         DEFAULT(false, false),
 786         SIZED(true, false),
 787         SIZED_EXACT(true, true);
 788 
 789         private final boolean sized;
 790         private final boolean exact;
 791 
 792         Mode(boolean sized, boolean exact) {
 793             this.sized = sized;
 794             this.exact = exact;
 795         }
 796 
 797         boolean isSized() {
 798             return sized;
 799         }
 800 
 801         boolean isExact() {
 802             return exact;
 803         }
 804     }
 805 
 806     /**
 807      * Bytecode StringBuilder strategy.
 808      *
 809      * <p>This strategy operates in three modes, gated by {@link Mode}.
 810      *
 811      * <p><b>{@link Strategy#BC_SB}: "bytecode StringBuilder".</b>
 812      *
 813      * <p>This strategy spins up the bytecode that has the same StringBuilder
 814      * chain javac would otherwise emit. This strategy uses only the public API,
 815      * and comes as the baseline for the current JDK behavior. On other words,
 816      * this strategy moves the javac generated bytecode to runtime. The
 817      * generated bytecode is loaded via Unsafe.defineAnonymousClass, but with
 818      * the caller class coming from the BSM -- in other words, the protection
 819      * guarantees are inherited from the method where invokedynamic was
 820      * originally called. This means, among other things, that the bytecode is
 821      * verified for all non-JDK uses.
 822      *
 823      * <p><b>{@link Strategy#BC_SB_SIZED}: "bytecode StringBuilder, but
 824      * sized".</b>
 825      *
 826      * <p>This strategy acts similarly to {@link Strategy#BC_SB}, but it also
 827      * tries to guess the capacity required for StringBuilder to accept all
 828      * arguments without resizing. This strategy only makes an educated guess:
 829      * it only guesses the space required for known types (e.g. primitives and
 830      * Strings), but does not otherwise convert arguments. Therefore, the
 831      * capacity estimate may be wrong, and StringBuilder may have to
 832      * transparently resize or trim when doing the actual concatenation. While
 833      * this does not constitute a correctness issue (in the end, that what BC_SB
 834      * has to do anyway), this does pose a potential performance problem.
 835      *
 836      * <p><b>{@link Strategy#BC_SB_SIZED_EXACT}: "bytecode StringBuilder, but
 837      * sized exactly".</b>
 838      *
 839      * <p>This strategy improves on @link Strategy#BC_SB_SIZED}, by first
 840      * converting all arguments to String in order to get the exact capacity
 841      * StringBuilder should have. The conversion is done via the public
 842      * String.valueOf and/or Object.toString methods, and does not touch any
 843      * private String API.
 844      */
 845     private static final class BytecodeStringBuilderStrategy {
 846         static final Unsafe UNSAFE = Unsafe.getUnsafe();
 847         static final int CLASSFILE_VERSION = 52;
 848         static final String METHOD_NAME = "concat";
 849 
 850         private BytecodeStringBuilderStrategy() {
 851             // no instantiation
 852         }
 853 
 854         private static MethodHandle generate(Lookup lookup, String className, MethodType args, Recipe recipe, Mode mode) throws Exception {
 855             ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
 856 
 857             cw.visit(CLASSFILE_VERSION,
 858                     ACC_SUPER + ACC_PUBLIC + ACC_FINAL + ACC_SYNTHETIC,
 859                     className,  // Unsafe.defineAnonymousClass would append an unique ID
 860                     null,
 861                     "java/lang/Object",
 862                     null
 863             );
 864 
 865             MethodVisitor mv = cw.visitMethod(
 866                     ACC_PUBLIC + ACC_STATIC + ACC_FINAL,
 867                     METHOD_NAME,
 868                     args.toMethodDescriptorString(),
 869                     null,
 870                     null);
 871 
 872             mv.visitAnnotation("Ljdk/internal/vm/annotation/ForceInline;", true);
 873             mv.visitCode();
 874 
 875             Class<?>[] arr = args.parameterArray();
 876             boolean[] guaranteedNonNull = new boolean[arr.length];
 877 
 878             if (mode.isExact()) {
 879                 /*
 880                     In exact mode, we need to convert all arguments to their String representations,
 881                     as this allows to compute their String sizes exactly. We cannot use private
 882                     methods for primitives in here, therefore we need to convert those as well.
 883 
 884                     We also record what arguments are guaranteed to be non-null as the result
 885                     of the conversion. String.valueOf does the null checks for us. The only
 886                     corner case to take care of is String.valueOf(Object) returning null itself.
 887 
 888                     Also, if any conversion happened, then the slot indices in the incoming
 889                     arguments are not equal to the final local maps. The only case this may break
 890                     is when converting 2-slot long/double argument to 1-slot String. Therefore,
 891                     we get away with tracking modified offset, since no conversion can overwrite
 892                     the upcoming the argument.
 893                  */
 894 
 895                 int off = 0;
 896                 int modOff = 0;
 897                 for (int c = 0; c < arr.length; c++) {
 898                     Class<?> cl = arr[c];
 899                     if (cl == String.class) {
 900                         if (off != modOff) {
 901                             mv.visitIntInsn(getLoadOpcode(cl), off);
 902                             mv.visitIntInsn(ASTORE, modOff);
 903                         }
 904                     } else {
 905                         mv.visitIntInsn(getLoadOpcode(cl), off);
 906                         mv.visitMethodInsn(
 907                                 INVOKESTATIC,
 908                                 "java/lang/String",
 909                                 "valueOf",
 910                                 getStringValueOfDesc(cl),
 911                                 false
 912                         );
 913                         mv.visitIntInsn(ASTORE, modOff);
 914                         arr[c] = String.class;
 915                         guaranteedNonNull[c] = cl.isPrimitive();
 916                     }
 917                     off += getParameterSize(cl);
 918                     modOff += getParameterSize(String.class);
 919                 }
 920             }
 921 
 922             if (mode.isSized()) {
 923                 /*
 924                     When operating in sized mode (this includes exact mode), it makes sense to make
 925                     StringBuilder append chains look familiar to OptimizeStringConcat. For that, we
 926                     need to do null-checks early, not make the append chain shape simpler.
 927                  */
 928 
 929                 int off = 0;
 930                 for (RecipeElement el : recipe.getElements()) {
 931                     switch (el.getTag()) {
 932                         case TAG_CONST:
 933                             // Guaranteed non-null, no null check required.
 934                             break;
 935                         case TAG_ARG:
 936                             // Null-checks are needed only for String arguments, and when a previous stage
 937                             // did not do implicit null-checks. If a String is null, we eagerly replace it
 938                             // with "null" constant. Note, we omit Objects here, because we don't call
 939                             // .length() on them down below.
 940                             int ac = el.getArgPos();
 941                             Class<?> cl = arr[ac];
 942                             if (cl == String.class && !guaranteedNonNull[ac]) {
 943                                 Label l0 = new Label();
 944                                 mv.visitIntInsn(ALOAD, off);
 945                                 mv.visitJumpInsn(IFNONNULL, l0);
 946                                 mv.visitLdcInsn("null");
 947                                 mv.visitIntInsn(ASTORE, off);
 948                                 mv.visitLabel(l0);
 949                             }
 950                             off += getParameterSize(cl);
 951                             break;
 952                         default:
 953                             throw new StringConcatException("Unhandled tag: " + el.getTag());
 954                     }
 955                 }
 956             }
 957 
 958             // Prepare StringBuilder instance
 959             mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
 960             mv.visitInsn(DUP);
 961 
 962             if (mode.isSized()) {
 963                 /*
 964                     Sized mode requires us to walk through the arguments, and estimate the final length.
 965                     In exact mode, this will operate on Strings only. This code would accumulate the
 966                     final length on stack.
 967                  */
 968                 int len = 0;
 969                 int off = 0;
 970 
 971                 mv.visitInsn(ICONST_0);
 972 
 973                 for (RecipeElement el : recipe.getElements()) {
 974                     switch (el.getTag()) {
 975                         case TAG_CONST:
 976                             len += el.getValue().length();
 977                             break;
 978                         case TAG_ARG:
 979                             /*
 980                                 If an argument is String, then we can call .length() on it. Sized/Exact modes have
 981                                 converted arguments for us. If an argument is primitive, we can provide a guess
 982                                 for its String representation size.
 983                             */
 984                             Class<?> cl = arr[el.getArgPos()];
 985                             if (cl == String.class) {
 986                                 mv.visitIntInsn(ALOAD, off);
 987                                 mv.visitMethodInsn(
 988                                         INVOKEVIRTUAL,
 989                                         "java/lang/String",
 990                                         "length",
 991                                         "()I",
 992                                         false
 993                                 );
 994                                 mv.visitInsn(IADD);
 995                             } else if (cl.isPrimitive()) {
 996                                 len += estimateSize(cl);
 997                             }
 998                             off += getParameterSize(cl);
 999                             break;
1000                         default:
1001                             throw new StringConcatException("Unhandled tag: " + el.getTag());
1002                     }
1003                 }
1004 
1005                 // Constants have non-zero length, mix in
1006                 if (len > 0) {
1007                     iconst(mv, len);
1008                     mv.visitInsn(IADD);
1009                 }
1010 
1011                 mv.visitMethodInsn(
1012                         INVOKESPECIAL,
1013                         "java/lang/StringBuilder",
1014                         "<init>",
1015                         "(I)V",
1016                         false
1017                 );
1018             } else {
1019                 mv.visitMethodInsn(
1020                         INVOKESPECIAL,
1021                         "java/lang/StringBuilder",
1022                         "<init>",
1023                         "()V",
1024                         false
1025                 );
1026             }
1027 
1028             // At this point, we have a blank StringBuilder on stack, fill it in with .append calls.
1029             {
1030                 int off = 0;
1031                 for (RecipeElement el : recipe.getElements()) {
1032                     String desc;
1033                     switch (el.getTag()) {
1034                         case TAG_CONST:
1035                             mv.visitLdcInsn(el.getValue());
1036                             desc = getSBAppendDesc(String.class);
1037                             break;
1038                         case TAG_ARG:
1039                             Class<?> cl = arr[el.getArgPos()];
1040                             mv.visitVarInsn(getLoadOpcode(cl), off);
1041                             off += getParameterSize(cl);
1042                             desc = getSBAppendDesc(cl);
1043                             break;
1044                         default:
1045                             throw new StringConcatException("Unhandled tag: " + el.getTag());
1046                     }
1047 
1048                     mv.visitMethodInsn(
1049                             INVOKEVIRTUAL,
1050                             "java/lang/StringBuilder",
1051                             "append",
1052                             desc,
1053                             false
1054                     );
1055                 }
1056             }
1057 
1058             if (DEBUG && mode.isExact()) {
1059                 /*
1060                     Exactness checks compare the final StringBuilder.capacity() with a resulting
1061                     String.length(). If these values disagree, that means StringBuilder had to perform
1062                     storage trimming, which defeats the purpose of exact strategies.
1063                  */
1064 
1065                 /*
1066                    The logic for this check is as follows:
1067 
1068                      Stack before:     Op:
1069                       (SB)              dup, dup
1070                       (SB, SB, SB)      capacity()
1071                       (int, SB, SB)     swap
1072                       (SB, int, SB)     toString()
1073                       (S, int, SB)      length()
1074                       (int, int, SB)    if_icmpeq
1075                       (SB)              <end>
1076 
1077                    Note that it leaves the same StringBuilder on exit, like the one on enter.
1078                  */
1079 
1080                 mv.visitInsn(DUP);
1081                 mv.visitInsn(DUP);
1082 
1083                 mv.visitMethodInsn(
1084                         INVOKEVIRTUAL,
1085                         "java/lang/StringBuilder",
1086                         "capacity",
1087                         "()I",
1088                         false
1089                 );
1090 
1091                 mv.visitInsn(SWAP);
1092 
1093                 mv.visitMethodInsn(
1094                         INVOKEVIRTUAL,
1095                         "java/lang/StringBuilder",
1096                         "toString",
1097                         "()Ljava/lang/String;",
1098                         false
1099                 );
1100 
1101                 mv.visitMethodInsn(
1102                         INVOKEVIRTUAL,
1103                         "java/lang/String",
1104                         "length",
1105                         "()I",
1106                         false
1107                 );
1108 
1109                 Label l0 = new Label();
1110                 mv.visitJumpInsn(IF_ICMPEQ, l0);
1111 
1112                 mv.visitTypeInsn(NEW, "java/lang/AssertionError");
1113                 mv.visitInsn(DUP);
1114                 mv.visitLdcInsn("Failed exactness check");
1115                 mv.visitMethodInsn(INVOKESPECIAL,
1116                         "java/lang/AssertionError",
1117                         "<init>",
1118                         "(Ljava/lang/Object;)V",
1119                         false);
1120                 mv.visitInsn(ATHROW);
1121 
1122                 mv.visitLabel(l0);
1123             }
1124 
1125             mv.visitMethodInsn(
1126                     INVOKEVIRTUAL,
1127                     "java/lang/StringBuilder",
1128                     "toString",
1129                     "()Ljava/lang/String;",
1130                     false
1131             );
1132 
1133             mv.visitInsn(ARETURN);
1134 
1135             mv.visitMaxs(-1, -1);
1136             mv.visitEnd();
1137             cw.visitEnd();
1138 
1139             byte[] classBytes = cw.toByteArray();
1140             try {
1141                 Class<?> hostClass = lookup.lookupClass();
1142                 Class<?> innerClass = UNSAFE.defineAnonymousClass(hostClass, classBytes, null);
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 }