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