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