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