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