rev 15544 : imported patch fold_select
rev 15545 : imported patch noperm

   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.org.objectweb.asm.ClassWriter;
  29 import jdk.internal.org.objectweb.asm.Label;
  30 import jdk.internal.org.objectweb.asm.MethodVisitor;
  31 import jdk.internal.org.objectweb.asm.Opcodes;
  32 import jdk.internal.vm.annotation.ForceInline;
  33 import jdk.internal.misc.Unsafe;
  34 
  35 import java.lang.invoke.MethodHandles.Lookup;
  36 import java.util.*;
  37 import java.util.concurrent.ConcurrentHashMap;
  38 import java.util.concurrent.ConcurrentMap;
  39 import java.util.function.Function;
  40 import sun.security.action.GetPropertyAction;
  41 
  42 import static jdk.internal.org.objectweb.asm.Opcodes.*;
  43 
  44 /**
  45  * <p>Methods to facilitate the creation of String concatenation methods, that
  46  * can be used to efficiently concatenate a known number of arguments of known
  47  * types, possibly after type adaptation and partial evaluation of arguments.
  48  * These methods are typically used as <em>bootstrap methods</em> for {@code
  49  * invokedynamic} call sites, to support the <em>string concatenation</em>
  50  * feature of the Java Programming Language.
  51  *
  52  * <p>Indirect access to the behavior specified by the provided {@code
  53  * MethodHandle} proceeds in order through two phases:
  54  *
  55  * <ol>
  56  *     <li><em>Linkage</em> occurs when the methods in this class are invoked.
  57  * They take as arguments a method type describing the concatenated arguments
  58  * count and types, and optionally the String <em>recipe</em>, plus the
  59  * constants that participate in the String concatenation. The details on
  60  * accepted recipe shapes are described further below. Linkage may involve
  61  * dynamically loading a new class that implements the expected concatenation
  62  * behavior. The {@code CallSite} holds the {@code MethodHandle} pointing to the
  63  * exact concatenation method. The concatenation methods may be shared among
  64  * different {@code CallSite}s, e.g. if linkage methods produce them as pure
  65  * functions.</li>
  66  *
  67  * <li><em>Invocation</em> occurs when a generated concatenation method is
  68  * invoked with the exact dynamic arguments. This may occur many times for a
  69  * single concatenation method. The method referenced by the behavior {@code
  70  * MethodHandle} is invoked with the static arguments and any additional dynamic
  71  * arguments provided on invocation, as if by {@link MethodHandle#invoke(Object...)}.</li>
  72  * </ol>
  73  *
  74  * <p> This class provides two forms of linkage methods: a simple version
  75  * ({@link #makeConcat(java.lang.invoke.MethodHandles.Lookup, String,
  76  * MethodType)}) using only the dynamic arguments, and an advanced version
  77  * ({@link #makeConcatWithConstants(java.lang.invoke.MethodHandles.Lookup,
  78  * String, MethodType, String, Object...)} using the advanced forms of capturing
  79  * the constant arguments. The advanced strategy can produce marginally better
  80  * invocation bytecode, at the expense of exploding the number of shapes of
  81  * string concatenation methods present at runtime, because those shapes would
  82  * include constant static arguments as well.
  83  *
  84  * @author Aleksey Shipilev
  85  * @author Remi Forax
  86  * @author Peter Levart
  87  *
  88  * @apiNote
  89  * <p>There is a JVM limit (classfile structural constraint): no method
  90  * can call with more than 255 slots. This limits the number of static and
  91  * dynamic arguments one can pass to bootstrap method. Since there are potential
  92  * concatenation strategies that use {@code MethodHandle} combinators, we need
  93  * to reserve a few empty slots on the parameter lists to capture the
  94  * temporal results. This is why bootstrap methods in this factory do not accept
  95  * more than 200 argument slots. Users requiring more than 200 argument slots in
  96  * concatenation are expected to split the large concatenation in smaller
  97  * expressions.
  98  *
  99  * @since 9
 100  */
 101 public final class StringConcatFactory {
 102 
 103     /**
 104      * Tag used to demarcate an ordinary argument.
 105      */
 106     private static final char TAG_ARG = '\u0001';
 107 
 108     /**
 109      * Tag used to demarcate a constant.
 110      */
 111     private static final char TAG_CONST = '\u0002';
 112 
 113     /**
 114      * Maximum number of argument slots in String Concat call.
 115      *
 116      * While the maximum number of argument slots that indy call can handle is 253,
 117      * we do not use all those slots, to let the strategies with MethodHandle
 118      * combinators to use some arguments.
 119      */
 120     private static final int MAX_INDY_CONCAT_ARG_SLOTS = 200;
 121 
 122     /**
 123      * Concatenation strategy to use. See {@link Strategy} for possible options.
 124      * This option is controllable with -Djava.lang.invoke.stringConcat JDK option.
 125      */
 126     private static Strategy STRATEGY;
 127 
 128     /**
 129      * Default strategy to use for concatenation.
 130      */
 131     private static final Strategy DEFAULT_STRATEGY = Strategy.MH_INLINE_SIZED_EXACT;
 132 
 133     private enum Strategy {
 134         /**
 135          * Bytecode generator, calling into {@link java.lang.StringBuilder}.
 136          */
 137         BC_SB,
 138 
 139         /**
 140          * Bytecode generator, calling into {@link java.lang.StringBuilder};
 141          * but trying to estimate the required storage.
 142          */
 143         BC_SB_SIZED,
 144 
 145         /**
 146          * Bytecode generator, calling into {@link java.lang.StringBuilder};
 147          * but computing the required storage exactly.
 148          */
 149         BC_SB_SIZED_EXACT,
 150 
 151         /**
 152          * MethodHandle-based generator, that in the end calls into {@link java.lang.StringBuilder}.
 153          * This strategy also tries to estimate the required storage.
 154          */
 155         MH_SB_SIZED,
 156 
 157         /**
 158          * MethodHandle-based generator, that in the end calls into {@link java.lang.StringBuilder}.
 159          * This strategy also estimate the required storage exactly.
 160          */
 161         MH_SB_SIZED_EXACT,
 162 
 163         /**
 164          * MethodHandle-based generator, that constructs its own byte[] array from
 165          * the arguments. It computes the required storage exactly.
 166          */
 167         MH_INLINE_SIZED_EXACT
 168     }
 169 
 170     /**
 171      * Enables debugging: this may print debugging messages, perform additional (non-neutral for performance)
 172      * checks, etc.
 173      */
 174     private static final boolean DEBUG;
 175 
 176     /**
 177      * Enables caching of strategy stubs. This may improve the linkage time by reusing the generated
 178      * code, at the expense of contaminating the profiles.
 179      */
 180     private static final boolean CACHE_ENABLE;
 181 
 182     private static final ConcurrentMap<Key, MethodHandle> CACHE;
 183 
 184     /**
 185      * Dump generated classes to disk, for debugging purposes.
 186      */
 187     private static final ProxyClassesDumper DUMPER;
 188 
 189     static {
 190         // In case we need to double-back onto the StringConcatFactory during this
 191         // static initialization, make sure we have the reasonable defaults to complete
 192         // the static initialization properly. After that, actual users would use the
 193         // the proper values we have read from the the properties.
 194         STRATEGY = DEFAULT_STRATEGY;
 195         // CACHE_ENABLE = false; // implied
 196         // CACHE = null;         // implied
 197         // DEBUG = false;        // implied
 198         // DUMPER = null;        // implied
 199 
 200         Properties props = GetPropertyAction.privilegedGetProperties();
 201         final String strategy =
 202                 props.getProperty("java.lang.invoke.stringConcat");
 203         CACHE_ENABLE = Boolean.parseBoolean(
 204                 props.getProperty("java.lang.invoke.stringConcat.cache"));
 205         DEBUG = Boolean.parseBoolean(
 206                 props.getProperty("java.lang.invoke.stringConcat.debug"));
 207         final String dumpPath =
 208                 props.getProperty("java.lang.invoke.stringConcat.dumpClasses");
 209 
 210         STRATEGY = (strategy == null) ? DEFAULT_STRATEGY : Strategy.valueOf(strategy);
 211         CACHE = CACHE_ENABLE ? new ConcurrentHashMap<>() : null;
 212         DUMPER = (dumpPath == null) ? null : ProxyClassesDumper.getInstance(dumpPath);
 213     }
 214 
 215     /**
 216      * Cache key is a composite of:
 217      *   - class name, that lets to disambiguate stubs, to avoid excess sharing
 218      *   - method type, describing the dynamic arguments for concatenation
 219      *   - concat recipe, describing the constants and concat shape
 220      */
 221     private static final class Key {
 222         final String className;
 223         final MethodType mt;
 224         final Recipe recipe;
 225 
 226         public Key(String className, MethodType mt, Recipe recipe) {
 227             this.className = className;
 228             this.mt = mt;
 229             this.recipe = recipe;
 230         }
 231 
 232         @Override
 233         public boolean equals(Object o) {
 234             if (this == o) return true;
 235             if (o == null || getClass() != o.getClass()) return false;
 236 
 237             Key key = (Key) o;
 238 
 239             if (!className.equals(key.className)) return false;
 240             if (!mt.equals(key.mt)) return false;
 241             if (!recipe.equals(key.recipe)) return false;
 242             return true;
 243         }
 244 
 245         @Override
 246         public int hashCode() {
 247             int result = className.hashCode();
 248             result = 31 * result + mt.hashCode();
 249             result = 31 * result + recipe.hashCode();
 250             return result;
 251         }
 252     }
 253 
 254     /**
 255      * Parses the recipe string, and produces the traversable collection of
 256      * {@link java.lang.invoke.StringConcatFactory.RecipeElement}-s for generator
 257      * strategies. Notably, this class parses out the constants from the recipe
 258      * and from other static arguments.
 259      */
 260     private static final class Recipe {
 261         private final List<RecipeElement> elements;
 262 
 263         public Recipe(String src, Object[] constants) {
 264             List<RecipeElement> el = new ArrayList<>();
 265 
 266             int constC = 0;
 267             int argC = 0;
 268 
 269             StringBuilder acc = new StringBuilder();
 270 
 271             for (int i = 0; i < src.length(); i++) {
 272                 char c = src.charAt(i);
 273 
 274                 if (c == TAG_CONST || c == TAG_ARG) {
 275                     // Detected a special tag, flush all accumulated characters
 276                     // as a constant first:
 277                     if (acc.length() > 0) {
 278                         el.add(new RecipeElement(acc.toString()));
 279                         acc.setLength(0);
 280                     }
 281                     if (c == TAG_CONST) {
 282                         Object cnst = constants[constC++];
 283                         el.add(new RecipeElement(cnst));
 284                     } else if (c == TAG_ARG) {
 285                         el.add(new RecipeElement(argC++));
 286                     }
 287                 } else {
 288                     // Not a special character, this is a constant embedded into
 289                     // the recipe itself.
 290                     acc.append(c);
 291                 }
 292             }
 293 
 294             // Flush the remaining characters as constant:
 295             if (acc.length() > 0) {
 296                 el.add(new RecipeElement(acc.toString()));
 297             }
 298 
 299             elements = el;
 300         }
 301 
 302         public List<RecipeElement> getElements() {
 303             return elements;
 304         }
 305 
 306         @Override
 307         public boolean equals(Object o) {
 308             if (this == o) return true;
 309             if (o == null || getClass() != o.getClass()) return false;
 310 
 311             Recipe recipe = (Recipe) o;
 312             return elements.equals(recipe.elements);
 313         }
 314 
 315         @Override
 316         public int hashCode() {
 317             return elements.hashCode();
 318         }
 319     }
 320 
 321     private static final class RecipeElement {
 322         private final Object value;
 323         private final int argPos;
 324         private final char tag;
 325 
 326         public RecipeElement(Object cnst) {
 327             this.value = Objects.requireNonNull(cnst);
 328             this.argPos = -1;
 329             this.tag = TAG_CONST;
 330         }
 331 
 332         public RecipeElement(int arg) {
 333             this.value = null;
 334             this.argPos = arg;
 335             this.tag = TAG_ARG;
 336         }
 337 
 338         public Object getValue() {
 339             assert (tag == TAG_CONST);
 340             return value;
 341         }
 342 
 343         public int getArgPos() {
 344             assert (tag == TAG_ARG);
 345             return argPos;
 346         }
 347 
 348         public char getTag() {
 349             return tag;
 350         }
 351 
 352         @Override
 353         public boolean equals(Object o) {
 354             if (this == o) return true;
 355             if (o == null || getClass() != o.getClass()) return false;
 356 
 357             RecipeElement that = (RecipeElement) o;
 358 
 359             if (this.tag != that.tag) return false;
 360             if (this.tag == TAG_CONST && (!value.equals(that.value))) return false;
 361             if (this.tag == TAG_ARG && (argPos != that.argPos)) return false;
 362             return true;
 363         }
 364 
 365         @Override
 366         public int hashCode() {
 367             return (int)tag;
 368         }
 369     }
 370 
 371     /**
 372      * Facilitates the creation of optimized String concatenation methods, that
 373      * can be used to efficiently concatenate a known number of arguments of
 374      * known types, possibly after type adaptation and partial evaluation of
 375      * arguments. Typically used as a <em>bootstrap method</em> for {@code
 376      * invokedynamic} call sites, to support the <em>string concatenation</em>
 377      * feature of the Java Programming Language.
 378      *
 379      * <p>When the target of the {@code CallSite} returned from this method is
 380      * invoked, it returns the result of String concatenation, taking all
 381      * function arguments passed to the linkage method as inputs for
 382      * concatenation. The target signature is given by {@code concatType}.
 383      * The arguments are concatenated as per requirements stated in JLS 15.18.1
 384      * "String Concatenation Operator +". Notably, the inputs are converted as
 385      * per JLS 5.1.11 "String Conversion", and combined from left to right.
 386      *
 387      * <p>Assume the linkage arguments are as follows:
 388      *
 389      * <ul>
 390      *     <li>{@code concatType}, describing the {@code CallSite} signature</li>
 391      * </ul>
 392      *
 393      * <p>Then the following linkage invariants must hold:
 394      *
 395      * <ul>
 396      *     <li>The parameter count in {@code concatType} is less than or equal to 200</li>
 397      *
 398      *     <li>The return type in {@code concatType} is assignable from {@link java.lang.String}</li>
 399      * </ul>
 400      *
 401      * @param lookup   Represents a lookup context with the accessibility
 402      *                 privileges of the caller.  When used with {@code
 403      *                 invokedynamic}, this is stacked automatically by the VM.
 404      * @param name     The name of the method to implement. This name is
 405      *                 arbitrary, and has no meaning for this linkage method.
 406      *                 When used with {@code invokedynamic}, this is provided by
 407      *                 the {@code NameAndType} of the {@code InvokeDynamic}
 408      *                 structure and is stacked automatically by the VM.
 409      * @param concatType The expected signature of the {@code CallSite}.  The
 410      *                   parameter types represent the types of concatenation
 411      *                   arguments; the return type is always assignable from {@link
 412      *                   java.lang.String}.  When used with {@code invokedynamic},
 413      *                   this is provided by the {@code NameAndType} of the {@code
 414      *                   InvokeDynamic} structure and is stacked automatically by
 415      *                   the VM.
 416      * @return a CallSite whose target can be used to perform String
 417      * concatenation, with dynamic concatenation arguments described by the given
 418      * {@code concatType}.
 419      * @throws StringConcatException If any of the linkage invariants described
 420      *                               here are violated.
 421      * @throws NullPointerException If any of the incoming arguments is null.
 422      *                              This will never happen when a bootstrap method
 423      *                              is called with invokedynamic.
 424      *
 425      * @jls  5.1.11 String Conversion
 426      * @jls 15.18.1 String Concatenation Operator +
 427      */
 428     public static CallSite makeConcat(MethodHandles.Lookup lookup,
 429                                       String name,
 430                                       MethodType concatType) throws StringConcatException {
 431         if (DEBUG) {
 432             System.out.println("StringConcatFactory " + STRATEGY + " is here for " + concatType);
 433         }
 434 
 435         return doStringConcat(lookup, name, concatType, true, null);
 436     }
 437 
 438     /**
 439      * Facilitates the creation of optimized String concatenation methods, that
 440      * can be used to efficiently concatenate a known number of arguments of
 441      * known types, possibly after type adaptation and partial evaluation of
 442      * arguments. Typically used as a <em>bootstrap method</em> for {@code
 443      * invokedynamic} call sites, to support the <em>string concatenation</em>
 444      * feature of the Java Programming Language.
 445      *
 446      * <p>When the target of the {@code CallSite} returned from this method is
 447      * invoked, it returns the result of String concatenation, taking all
 448      * function arguments and constants passed to the linkage method as inputs for
 449      * concatenation. The target signature is given by {@code concatType}, and
 450      * does not include constants. The arguments are concatenated as per requirements
 451      * stated in JLS 15.18.1 "String Concatenation Operator +". Notably, the inputs
 452      * are converted as per JLS 5.1.11 "String Conversion", and combined from left
 453      * to right.
 454      *
 455      * <p>The concatenation <em>recipe</em> is a String description for the way to
 456      * construct a concatenated String from the arguments and constants. The
 457      * recipe is processed from left to right, and each character represents an
 458      * input to concatenation. Recipe characters mean:
 459      *
 460      * <ul>
 461      *
 462      *   <li><em>\1 (Unicode point 0001)</em>: an ordinary argument. This
 463      *   input is passed through dynamic argument, and is provided during the
 464      *   concatenation method invocation. This input can be null.</li>
 465      *
 466      *   <li><em>\2 (Unicode point 0002):</em> a constant. This input passed
 467      *   through static bootstrap argument. This constant can be any value
 468      *   representable in constant pool. If necessary, the factory would call
 469      *   {@code toString} to perform a one-time String conversion.</li>
 470      *
 471      *   <li><em>Any other char value:</em> a single character constant.</li>
 472      * </ul>
 473      *
 474      * <p>Assume the linkage arguments are as follows:
 475      *
 476      * <ul>
 477      *   <li>{@code concatType}, describing the {@code CallSite} signature</li>
 478      *   <li>{@code recipe}, describing the String recipe</li>
 479      *   <li>{@code constants}, the vararg array of constants</li>
 480      * </ul>
 481      *
 482      * <p>Then the following linkage invariants must hold:
 483      *
 484      * <ul>
 485      *   <li>The parameter count in {@code concatType} is less than or equal to
 486      *   200</li>
 487      *
 488      *   <li>The parameter count in {@code concatType} equals to number of \1 tags
 489      *   in {@code recipe}</li>
 490      *
 491      *   <li>The return type in {@code concatType} is assignable
 492      *   from {@link java.lang.String}, and matches the return type of the
 493      *   returned {@link MethodHandle}</li>
 494      *
 495      *   <li>The number of elements in {@code constants} equals to number of \2
 496      *   tags in {@code recipe}</li>
 497      * </ul>
 498      *
 499      * @param lookup    Represents a lookup context with the accessibility
 500      *                  privileges of the caller. When used with {@code
 501      *                  invokedynamic}, this is stacked automatically by the
 502      *                  VM.
 503      * @param name      The name of the method to implement. This name is
 504      *                  arbitrary, and has no meaning for this linkage method.
 505      *                  When used with {@code invokedynamic}, this is provided
 506      *                  by the {@code NameAndType} of the {@code InvokeDynamic}
 507      *                  structure and is stacked automatically by the VM.
 508      * @param concatType The expected signature of the {@code CallSite}.  The
 509      *                  parameter types represent the types of dynamic concatenation
 510      *                  arguments; the return type is always assignable from {@link
 511      *                  java.lang.String}.  When used with {@code
 512      *                  invokedynamic}, this is provided by the {@code
 513      *                  NameAndType} of the {@code InvokeDynamic} structure and
 514      *                  is stacked automatically by the VM.
 515      * @param recipe    Concatenation recipe, described above.
 516      * @param constants A vararg parameter representing the constants passed to
 517      *                  the linkage method.
 518      * @return a CallSite whose target can be used to perform String
 519      * concatenation, with dynamic concatenation arguments described by the given
 520      * {@code concatType}.
 521      * @throws StringConcatException If any of the linkage invariants described
 522      *                               here are violated.
 523      * @throws NullPointerException If any of the incoming arguments is null, or
 524      *                              any constant in {@code recipe} is null.
 525      *                              This will never happen when a bootstrap method
 526      *                              is called with invokedynamic.
 527      * @apiNote Code generators have three distinct ways to process a constant
 528      * string operand S in a string concatenation expression.  First, S can be
 529      * materialized as a reference (using ldc) and passed as an ordinary argument
 530      * (recipe '\1'). Or, S can be stored in the constant pool and passed as a
 531      * constant (recipe '\2') . Finally, if S contains neither of the recipe
 532      * tag characters ('\1', '\2') then S can be interpolated into the recipe
 533      * itself, causing its characters to be inserted into the result.
 534      *
 535      * @jls  5.1.11 String Conversion
 536      * @jls 15.18.1 String Concatenation Operator +
 537      */
 538     public static CallSite makeConcatWithConstants(MethodHandles.Lookup lookup,
 539                                                    String name,
 540                                                    MethodType concatType,
 541                                                    String recipe,
 542                                                    Object... constants) throws StringConcatException {
 543         if (DEBUG) {
 544             System.out.println("StringConcatFactory " + STRATEGY + " is here for " + concatType + ", {" + recipe + "}, " + Arrays.toString(constants));
 545         }
 546 
 547         return doStringConcat(lookup, name, concatType, false, recipe, constants);
 548     }
 549 
 550     private static CallSite doStringConcat(MethodHandles.Lookup lookup,
 551                                            String name,
 552                                            MethodType concatType,
 553                                            boolean generateRecipe,
 554                                            String recipe,
 555                                            Object... constants) throws StringConcatException {
 556         Objects.requireNonNull(lookup, "Lookup is null");
 557         Objects.requireNonNull(name, "Name is null");
 558         Objects.requireNonNull(concatType, "Concat type is null");
 559         Objects.requireNonNull(constants, "Constants are null");
 560 
 561         for (Object o : constants) {
 562             Objects.requireNonNull(o, "Cannot accept null constants");
 563         }
 564 
 565         if ((lookup.lookupModes() & MethodHandles.Lookup.PRIVATE) == 0) {
 566             throw new StringConcatException("Invalid caller: " +
 567                     lookup.lookupClass().getName());

 568         }
 569 
 570         int cCount = 0;
 571         int oCount = 0;
 572         if (generateRecipe) {
 573             // Mock the recipe to reuse the concat generator code
 574             char[] value = new char[concatType.parameterCount()];
 575             Arrays.fill(value, TAG_ARG);
 576             recipe = new String(value);
 577             oCount = concatType.parameterCount();
 578         } else {
 579             Objects.requireNonNull(recipe, "Recipe is null");
 580 
 581             for (int i = 0; i < recipe.length(); i++) {
 582                 char c = recipe.charAt(i);
 583                 if (c == TAG_CONST) cCount++;
 584                 if (c == TAG_ARG)   oCount++;
 585             }
 586         }
 587 
 588         if (oCount != concatType.parameterCount()) {
 589             throw new StringConcatException(
 590                     "Mismatched number of concat arguments: recipe wants " +
 591                             oCount +
 592                             " arguments, but signature provides " +
 593                             concatType.parameterCount());
 594         }
 595 
 596         if (cCount != constants.length) {
 597             throw new StringConcatException(
 598                     "Mismatched number of concat constants: recipe wants " +
 599                             cCount +
 600                             " constants, but only " +
 601                             constants.length +
 602                             " are passed");
 603         }
 604 
 605         if (!concatType.returnType().isAssignableFrom(String.class)) {
 606             throw new StringConcatException(
 607                     "The return type should be compatible with String, but it is " +
 608                             concatType.returnType());
 609         }
 610 
 611         if (concatType.parameterCount() > MAX_INDY_CONCAT_ARG_SLOTS) {
 612             throw new StringConcatException("Too many concat argument slots: " +
 613                     concatType.parameterCount() +
 614                     ", can only accept " +
 615                     MAX_INDY_CONCAT_ARG_SLOTS);
 616         }
 617 
 618         String className = getClassName(lookup.lookupClass());
 619         MethodType mt = adaptType(concatType);
 620         Recipe rec = new Recipe(recipe, constants);
 621 
 622         MethodHandle mh;
 623         if (CACHE_ENABLE) {
 624             Key key = new Key(className, mt, rec);
 625             mh = CACHE.get(key);
 626             if (mh == null) {
 627                 mh = generate(lookup, className, mt, rec);
 628                 CACHE.put(key, mh);
 629             }
 630         } else {
 631             mh = generate(lookup, className, mt, rec);
 632         }
 633         return new ConstantCallSite(mh.asType(concatType));
 634     }
 635 
 636     /**
 637      * Adapt method type to an API we are going to use.
 638      *
 639      * This strips the concrete classes from the signatures, thus preventing
 640      * class leakage when we cache the concatenation stubs.
 641      *
 642      * @param args actual argument types
 643      * @return argument types the strategy is going to use
 644      */
 645     private static MethodType adaptType(MethodType args) {
 646         Class<?>[] ptypes = null;
 647         for (int i = 0; i < args.parameterCount(); i++) {
 648             Class<?> ptype = args.parameterType(i);
 649             if (!ptype.isPrimitive() &&
 650                     ptype != String.class &&
 651                     ptype != Object.class) { // truncate to Object
 652                 if (ptypes == null) {
 653                     ptypes = args.parameterArray();
 654                 }
 655                 ptypes[i] = Object.class;
 656             }
 657             // else other primitives or String or Object (unchanged)
 658         }
 659         return (ptypes != null)
 660                 ? MethodType.methodType(args.returnType(), ptypes)
 661                 : args;
 662     }
 663 
 664     private static String getClassName(Class<?> hostClass) throws StringConcatException {
 665         /*
 666           When cache is enabled, we want to cache as much as we can.
 667 
 668           However, there are two peculiarities:
 669 
 670            a) The generated class should stay within the same package as the
 671               host class, to allow Unsafe.defineAnonymousClass access controls
 672               to work properly. JDK may choose to fail with IllegalAccessException
 673               when accessing a VM anonymous class with non-privileged callers,
 674               see JDK-8058575.
 675 
 676            b) If we mark the stub with some prefix, say, derived from the package
 677               name because of (a), we can technically use that stub in other packages.
 678               But the call stack traces would be extremely puzzling to unsuspecting users
 679               and profiling tools: whatever stub wins the race, would be linked in all
 680               similar callsites.
 681 
 682            Therefore, we set the class prefix to match the host class package, and use
 683            the prefix as the cache key too. This only affects BC_* strategies, and only when
 684            cache is enabled.
 685          */
 686 
 687         switch (STRATEGY) {
 688             case BC_SB:
 689             case BC_SB_SIZED:
 690             case BC_SB_SIZED_EXACT: {
 691                 if (CACHE_ENABLE) {
 692                     String pkgName = hostClass.getPackageName();
 693                     return (pkgName != null && !pkgName.isEmpty() ? pkgName.replace('.', '/') + "/" : "") + "Stubs$$StringConcat";
 694                 } else {
 695                     return hostClass.getName().replace('.', '/') + "$$StringConcat";
 696                 }
 697             }
 698             case MH_SB_SIZED:
 699             case MH_SB_SIZED_EXACT:
 700             case MH_INLINE_SIZED_EXACT:
 701                 // MethodHandle strategies do not need a class name.
 702                 return "";
 703             default:
 704                 throw new StringConcatException("Concatenation strategy " + STRATEGY + " is not implemented");
 705         }
 706     }
 707 
 708     private static MethodHandle generate(Lookup lookup, String className, MethodType mt, Recipe recipe) throws StringConcatException {
 709         try {
 710             switch (STRATEGY) {
 711                 case BC_SB:
 712                     return BytecodeStringBuilderStrategy.generate(lookup, className, mt, recipe, Mode.DEFAULT);
 713                 case BC_SB_SIZED:
 714                     return BytecodeStringBuilderStrategy.generate(lookup, className, mt, recipe, Mode.SIZED);
 715                 case BC_SB_SIZED_EXACT:
 716                     return BytecodeStringBuilderStrategy.generate(lookup, className, mt, recipe, Mode.SIZED_EXACT);
 717                 case MH_SB_SIZED:
 718                     return MethodHandleStringBuilderStrategy.generate(mt, recipe, Mode.SIZED);
 719                 case MH_SB_SIZED_EXACT:
 720                     return MethodHandleStringBuilderStrategy.generate(mt, recipe, Mode.SIZED_EXACT);
 721                 case MH_INLINE_SIZED_EXACT:
 722                     return MethodHandleInlineCopyStrategy.generate(mt, recipe);
 723                 default:
 724                     throw new StringConcatException("Concatenation strategy " + STRATEGY + " is not implemented");
 725             }
 726         } catch (Throwable t) {
 727             throw new StringConcatException("Generator failed", t);
 728         }
 729     }
 730 
 731     private enum Mode {
 732         DEFAULT(false, false),
 733         SIZED(true, false),
 734         SIZED_EXACT(true, true);
 735 
 736         private final boolean sized;
 737         private final boolean exact;
 738 
 739         Mode(boolean sized, boolean exact) {
 740             this.sized = sized;
 741             this.exact = exact;
 742         }
 743 
 744         boolean isSized() {
 745             return sized;
 746         }
 747 
 748         boolean isExact() {
 749             return exact;
 750         }
 751     }
 752 
 753     /**
 754      * Bytecode StringBuilder strategy.
 755      *
 756      * <p>This strategy operates in three modes, gated by {@link Mode}.
 757      *
 758      * <p><b>{@link Strategy#BC_SB}: "bytecode StringBuilder".</b>
 759      *
 760      * <p>This strategy spins up the bytecode that has the same StringBuilder
 761      * chain javac would otherwise emit. This strategy uses only the public API,
 762      * and comes as the baseline for the current JDK behavior. On other words,
 763      * this strategy moves the javac generated bytecode to runtime. The
 764      * generated bytecode is loaded via Unsafe.defineAnonymousClass, but with
 765      * the caller class coming from the BSM -- in other words, the protection
 766      * guarantees are inherited from the method where invokedynamic was
 767      * originally called. This means, among other things, that the bytecode is
 768      * verified for all non-JDK uses.
 769      *
 770      * <p><b>{@link Strategy#BC_SB_SIZED}: "bytecode StringBuilder, but
 771      * sized".</b>
 772      *
 773      * <p>This strategy acts similarly to {@link Strategy#BC_SB}, but it also
 774      * tries to guess the capacity required for StringBuilder to accept all
 775      * arguments without resizing. This strategy only makes an educated guess:
 776      * it only guesses the space required for known types (e.g. primitives and
 777      * Strings), but does not otherwise convert arguments. Therefore, the
 778      * capacity estimate may be wrong, and StringBuilder may have to
 779      * transparently resize or trim when doing the actual concatenation. While
 780      * this does not constitute a correctness issue (in the end, that what BC_SB
 781      * has to do anyway), this does pose a potential performance problem.
 782      *
 783      * <p><b>{@link Strategy#BC_SB_SIZED_EXACT}: "bytecode StringBuilder, but
 784      * sized exactly".</b>
 785      *
 786      * <p>This strategy improves on @link Strategy#BC_SB_SIZED}, by first
 787      * converting all arguments to String in order to get the exact capacity
 788      * StringBuilder should have. The conversion is done via the public
 789      * String.valueOf and/or Object.toString methods, and does not touch any
 790      * private String API.
 791      */
 792     private static final class BytecodeStringBuilderStrategy {
 793         static final Unsafe UNSAFE = Unsafe.getUnsafe();
 794         static final int CLASSFILE_VERSION = 52;
 795         static final String METHOD_NAME = "concat";
 796 
 797         private BytecodeStringBuilderStrategy() {
 798             // no instantiation
 799         }
 800 
 801         private static MethodHandle generate(Lookup lookup, String className, MethodType args, Recipe recipe, Mode mode) throws Exception {
 802             ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
 803 
 804             cw.visit(CLASSFILE_VERSION,
 805                     ACC_SUPER + ACC_PUBLIC + ACC_FINAL + ACC_SYNTHETIC,
 806                     className,  // Unsafe.defineAnonymousClass would append an unique ID
 807                     null,
 808                     "java/lang/Object",
 809                     null
 810             );
 811 
 812             MethodVisitor mv = cw.visitMethod(
 813                     ACC_PUBLIC + ACC_STATIC + ACC_FINAL,
 814                     METHOD_NAME,
 815                     args.toMethodDescriptorString(),
 816                     null,
 817                     null);
 818 
 819             mv.visitAnnotation("Ljdk/internal/vm/annotation/ForceInline;", true);
 820             mv.visitCode();
 821 
 822             Class<?>[] arr = args.parameterArray();
 823             boolean[] guaranteedNonNull = new boolean[arr.length];
 824 
 825             if (mode.isExact()) {
 826                 /*
 827                     In exact mode, we need to convert all arguments to their String representations,
 828                     as this allows to compute their String sizes exactly. We cannot use private
 829                     methods for primitives in here, therefore we need to convert those as well.
 830 
 831                     We also record what arguments are guaranteed to be non-null as the result
 832                     of the conversion. String.valueOf does the null checks for us. The only
 833                     corner case to take care of is String.valueOf(Object) returning null itself.
 834 
 835                     Also, if any conversion happened, then the slot indices in the incoming
 836                     arguments are not equal to the final local maps. The only case this may break
 837                     is when converting 2-slot long/double argument to 1-slot String. Therefore,
 838                     we get away with tracking modified offset, since no conversion can overwrite
 839                     the upcoming the argument.
 840                  */
 841 
 842                 int off = 0;
 843                 int modOff = 0;
 844                 for (int c = 0; c < arr.length; c++) {
 845                     Class<?> cl = arr[c];
 846                     if (cl == String.class) {
 847                         if (off != modOff) {
 848                             mv.visitIntInsn(getLoadOpcode(cl), off);
 849                             mv.visitIntInsn(ASTORE, modOff);
 850                         }
 851                     } else {
 852                         mv.visitIntInsn(getLoadOpcode(cl), off);
 853                         mv.visitMethodInsn(
 854                                 INVOKESTATIC,
 855                                 "java/lang/String",
 856                                 "valueOf",
 857                                 getStringValueOfDesc(cl),
 858                                 false
 859                         );
 860                         mv.visitIntInsn(ASTORE, modOff);
 861                         arr[c] = String.class;
 862                         guaranteedNonNull[c] = cl.isPrimitive();
 863                     }
 864                     off += getParameterSize(cl);
 865                     modOff += getParameterSize(String.class);
 866                 }
 867             }
 868 
 869             if (mode.isSized()) {
 870                 /*
 871                     When operating in sized mode (this includes exact mode), it makes sense to make
 872                     StringBuilder append chains look familiar to OptimizeStringConcat. For that, we
 873                     need to do null-checks early, not make the append chain shape simpler.
 874                  */
 875 
 876                 int off = 0;
 877                 for (RecipeElement el : recipe.getElements()) {
 878                     switch (el.getTag()) {
 879                         case TAG_CONST:
 880                             // Guaranteed non-null, no null check required.
 881                             break;
 882                         case TAG_ARG:
 883                             // Null-checks are needed only for String arguments, and when a previous stage
 884                             // did not do implicit null-checks. If a String is null, we eagerly replace it
 885                             // with "null" constant. Note, we omit Objects here, because we don't call
 886                             // .length() on them down below.
 887                             int ac = el.getArgPos();
 888                             Class<?> cl = arr[ac];
 889                             if (cl == String.class && !guaranteedNonNull[ac]) {
 890                                 Label l0 = new Label();
 891                                 mv.visitIntInsn(ALOAD, off);
 892                                 mv.visitJumpInsn(IFNONNULL, l0);
 893                                 mv.visitLdcInsn("null");
 894                                 mv.visitIntInsn(ASTORE, off);
 895                                 mv.visitLabel(l0);
 896                             }
 897                             off += getParameterSize(cl);
 898                             break;
 899                         default:
 900                             throw new StringConcatException("Unhandled tag: " + el.getTag());
 901                     }
 902                 }
 903             }
 904 
 905             // Prepare StringBuilder instance
 906             mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
 907             mv.visitInsn(DUP);
 908 
 909             if (mode.isSized()) {
 910                 /*
 911                     Sized mode requires us to walk through the arguments, and estimate the final length.
 912                     In exact mode, this will operate on Strings only. This code would accumulate the
 913                     final length on stack.
 914                  */
 915                 int len = 0;
 916                 int off = 0;
 917 
 918                 mv.visitInsn(ICONST_0);
 919 
 920                 for (RecipeElement el : recipe.getElements()) {
 921                     switch (el.getTag()) {
 922                         case TAG_CONST:
 923                             Object cnst = el.getValue();
 924                             len += cnst.toString().length();
 925                             break;
 926                         case TAG_ARG:
 927                             /*
 928                                 If an argument is String, then we can call .length() on it. Sized/Exact modes have
 929                                 converted arguments for us. If an argument is primitive, we can provide a guess
 930                                 for its String representation size.
 931                             */
 932                             Class<?> cl = arr[el.getArgPos()];
 933                             if (cl == String.class) {
 934                                 mv.visitIntInsn(ALOAD, off);
 935                                 mv.visitMethodInsn(
 936                                         INVOKEVIRTUAL,
 937                                         "java/lang/String",
 938                                         "length",
 939                                         "()I",
 940                                         false
 941                                 );
 942                                 mv.visitInsn(IADD);
 943                             } else if (cl.isPrimitive()) {
 944                                 len += estimateSize(cl);
 945                             }
 946                             off += getParameterSize(cl);
 947                             break;
 948                         default:
 949                             throw new StringConcatException("Unhandled tag: " + el.getTag());
 950                     }
 951                 }
 952 
 953                 // Constants have non-zero length, mix in
 954                 if (len > 0) {
 955                     iconst(mv, len);
 956                     mv.visitInsn(IADD);
 957                 }
 958 
 959                 mv.visitMethodInsn(
 960                         INVOKESPECIAL,
 961                         "java/lang/StringBuilder",
 962                         "<init>",
 963                         "(I)V",
 964                         false
 965                 );
 966             } else {
 967                 mv.visitMethodInsn(
 968                         INVOKESPECIAL,
 969                         "java/lang/StringBuilder",
 970                         "<init>",
 971                         "()V",
 972                         false
 973                 );
 974             }
 975 
 976             // At this point, we have a blank StringBuilder on stack, fill it in with .append calls.
 977             {
 978                 int off = 0;
 979                 for (RecipeElement el : recipe.getElements()) {
 980                     String desc;
 981                     switch (el.getTag()) {
 982                         case TAG_CONST:
 983                             Object cnst = el.getValue();
 984                             mv.visitLdcInsn(cnst);
 985                             desc = getSBAppendDesc(cnst.getClass());
 986                             break;
 987                         case TAG_ARG:
 988                             Class<?> cl = arr[el.getArgPos()];
 989                             mv.visitVarInsn(getLoadOpcode(cl), off);
 990                             off += getParameterSize(cl);
 991                             desc = getSBAppendDesc(cl);
 992                             break;
 993                         default:
 994                             throw new StringConcatException("Unhandled tag: " + el.getTag());
 995                     }
 996 
 997                     mv.visitMethodInsn(
 998                             INVOKEVIRTUAL,
 999                             "java/lang/StringBuilder",
1000                             "append",
1001                             desc,
1002                             false
1003                     );
1004                 }
1005             }
1006 
1007             if (DEBUG && mode.isExact()) {
1008                 /*
1009                     Exactness checks compare the final StringBuilder.capacity() with a resulting
1010                     String.length(). If these values disagree, that means StringBuilder had to perform
1011                     storage trimming, which defeats the purpose of exact strategies.
1012                  */
1013 
1014                 /*
1015                    The logic for this check is as follows:
1016 
1017                      Stack before:     Op:
1018                       (SB)              dup, dup
1019                       (SB, SB, SB)      capacity()
1020                       (int, SB, SB)     swap
1021                       (SB, int, SB)     toString()
1022                       (S, int, SB)      length()
1023                       (int, int, SB)    if_icmpeq
1024                       (SB)              <end>
1025 
1026                    Note that it leaves the same StringBuilder on exit, like the one on enter.
1027                  */
1028 
1029                 mv.visitInsn(DUP);
1030                 mv.visitInsn(DUP);
1031 
1032                 mv.visitMethodInsn(
1033                         INVOKEVIRTUAL,
1034                         "java/lang/StringBuilder",
1035                         "capacity",
1036                         "()I",
1037                         false
1038                 );
1039 
1040                 mv.visitInsn(SWAP);
1041 
1042                 mv.visitMethodInsn(
1043                         INVOKEVIRTUAL,
1044                         "java/lang/StringBuilder",
1045                         "toString",
1046                         "()Ljava/lang/String;",
1047                         false
1048                 );
1049 
1050                 mv.visitMethodInsn(
1051                         INVOKEVIRTUAL,
1052                         "java/lang/String",
1053                         "length",
1054                         "()I",
1055                         false
1056                 );
1057 
1058                 Label l0 = new Label();
1059                 mv.visitJumpInsn(IF_ICMPEQ, l0);
1060 
1061                 mv.visitTypeInsn(NEW, "java/lang/AssertionError");
1062                 mv.visitInsn(DUP);
1063                 mv.visitLdcInsn("Failed exactness check");
1064                 mv.visitMethodInsn(INVOKESPECIAL,
1065                         "java/lang/AssertionError",
1066                         "<init>",
1067                         "(Ljava/lang/Object;)V",
1068                         false);
1069                 mv.visitInsn(ATHROW);
1070 
1071                 mv.visitLabel(l0);
1072             }
1073 
1074             mv.visitMethodInsn(
1075                     INVOKEVIRTUAL,
1076                     "java/lang/StringBuilder",
1077                     "toString",
1078                     "()Ljava/lang/String;",
1079                     false
1080             );
1081 
1082             mv.visitInsn(ARETURN);
1083 
1084             mv.visitMaxs(-1, -1);
1085             mv.visitEnd();
1086             cw.visitEnd();
1087 
1088             byte[] classBytes = cw.toByteArray();
1089             try {
1090                 Class<?> hostClass = lookup.lookupClass();
1091                 Class<?> innerClass = UNSAFE.defineAnonymousClass(hostClass, classBytes, null);
1092                 UNSAFE.ensureClassInitialized(innerClass);
1093                 dumpIfEnabled(innerClass.getName(), classBytes);
1094                 return Lookup.IMPL_LOOKUP.findStatic(innerClass, METHOD_NAME, args);
1095             } catch (Throwable e) {
1096                 dumpIfEnabled(className + "$$FAILED", classBytes);
1097                 throw new StringConcatException("Error while spinning the class", e);
1098             }
1099         }
1100 
1101         private static void dumpIfEnabled(String name, byte[] bytes) {
1102             if (DUMPER != null) {
1103                 DUMPER.dumpClass(name, bytes);
1104             }
1105         }
1106 
1107         private static String getSBAppendDesc(Class<?> cl) {
1108             if (cl.isPrimitive()) {
1109                 if (cl == Integer.TYPE || cl == Byte.TYPE || cl == Short.TYPE) {
1110                     return "(I)Ljava/lang/StringBuilder;";
1111                 } else if (cl == Boolean.TYPE) {
1112                     return "(Z)Ljava/lang/StringBuilder;";
1113                 } else if (cl == Character.TYPE) {
1114                     return "(C)Ljava/lang/StringBuilder;";
1115                 } else if (cl == Double.TYPE) {
1116                     return "(D)Ljava/lang/StringBuilder;";
1117                 } else if (cl == Float.TYPE) {
1118                     return "(F)Ljava/lang/StringBuilder;";
1119                 } else if (cl == Long.TYPE) {
1120                     return "(J)Ljava/lang/StringBuilder;";
1121                 } else {
1122                     throw new IllegalStateException("Unhandled primitive StringBuilder.append: " + cl);
1123                 }
1124             } else if (cl == String.class) {
1125                 return "(Ljava/lang/String;)Ljava/lang/StringBuilder;";
1126             } else {
1127                 return "(Ljava/lang/Object;)Ljava/lang/StringBuilder;";
1128             }
1129         }
1130 
1131         private static String getStringValueOfDesc(Class<?> cl) {
1132             if (cl.isPrimitive()) {
1133                 if (cl == Integer.TYPE || cl == Byte.TYPE || cl == Short.TYPE) {
1134                     return "(I)Ljava/lang/String;";
1135                 } else if (cl == Boolean.TYPE) {
1136                     return "(Z)Ljava/lang/String;";
1137                 } else if (cl == Character.TYPE) {
1138                     return "(C)Ljava/lang/String;";
1139                 } else if (cl == Double.TYPE) {
1140                     return "(D)Ljava/lang/String;";
1141                 } else if (cl == Float.TYPE) {
1142                     return "(F)Ljava/lang/String;";
1143                 } else if (cl == Long.TYPE) {
1144                     return "(J)Ljava/lang/String;";
1145                 } else {
1146                     throw new IllegalStateException("Unhandled String.valueOf: " + cl);
1147                 }
1148             } else if (cl == String.class) {
1149                 return "(Ljava/lang/String;)Ljava/lang/String;";
1150             } else {
1151                 return "(Ljava/lang/Object;)Ljava/lang/String;";
1152             }
1153         }
1154 
1155         /**
1156          * The following method is copied from
1157          * org.objectweb.asm.commons.InstructionAdapter. Part of ASM: a very small
1158          * and fast Java bytecode manipulation framework.
1159          * Copyright (c) 2000-2005 INRIA, France Telecom All rights reserved.
1160          */
1161         private static void iconst(MethodVisitor mv, final int cst) {
1162             if (cst >= -1 && cst <= 5) {
1163                 mv.visitInsn(Opcodes.ICONST_0 + cst);
1164             } else if (cst >= Byte.MIN_VALUE && cst <= Byte.MAX_VALUE) {
1165                 mv.visitIntInsn(Opcodes.BIPUSH, cst);
1166             } else if (cst >= Short.MIN_VALUE && cst <= Short.MAX_VALUE) {
1167                 mv.visitIntInsn(Opcodes.SIPUSH, cst);
1168             } else {
1169                 mv.visitLdcInsn(cst);
1170             }
1171         }
1172 
1173         private static int getLoadOpcode(Class<?> c) {
1174             if (c == Void.TYPE) {
1175                 throw new InternalError("Unexpected void type of load opcode");
1176             }
1177             return ILOAD + getOpcodeOffset(c);
1178         }
1179 
1180         private static int getOpcodeOffset(Class<?> c) {
1181             if (c.isPrimitive()) {
1182                 if (c == Long.TYPE) {
1183                     return 1;
1184                 } else if (c == Float.TYPE) {
1185                     return 2;
1186                 } else if (c == Double.TYPE) {
1187                     return 3;
1188                 }
1189                 return 0;
1190             } else {
1191                 return 4;
1192             }
1193         }
1194 
1195         private static int getParameterSize(Class<?> c) {
1196             if (c == Void.TYPE) {
1197                 return 0;
1198             } else if (c == Long.TYPE || c == Double.TYPE) {
1199                 return 2;
1200             }
1201             return 1;
1202         }
1203     }
1204 
1205     /**
1206      * MethodHandle StringBuilder strategy.
1207      *
1208      * <p>This strategy operates in two modes, gated by {@link Mode}.
1209      *
1210      * <p><b>{@link Strategy#MH_SB_SIZED}: "MethodHandles StringBuilder,
1211      * sized".</b>
1212      *
1213      * <p>This strategy avoids spinning up the bytecode by building the
1214      * computation on MethodHandle combinators. The computation is built with
1215      * public MethodHandle APIs, resolved from a public Lookup sequence, and
1216      * ends up calling the public StringBuilder API. Therefore, this strategy
1217      * does not use any private API at all, even the Unsafe.defineAnonymousClass,
1218      * since everything is handled under cover by java.lang.invoke APIs.
1219      *
1220      * <p><b>{@link Strategy#MH_SB_SIZED_EXACT}: "MethodHandles StringBuilder,
1221      * sized exactly".</b>
1222      *
1223      * <p>This strategy improves on @link Strategy#MH_SB_SIZED}, by first
1224      * converting all arguments to String in order to get the exact capacity
1225      * StringBuilder should have. The conversion is done via the public
1226      * String.valueOf and/or Object.toString methods, and does not touch any
1227      * private String API.
1228      */
1229     private static final class MethodHandleStringBuilderStrategy {
1230 
1231         private MethodHandleStringBuilderStrategy() {
1232             // no instantiation
1233         }
1234 
1235         private static MethodHandle generate(MethodType mt, Recipe recipe, Mode mode) throws Exception {
1236             int pc = mt.parameterCount();
1237 
1238             Class<?>[] ptypes = mt.parameterArray();
1239             MethodHandle[] filters = new MethodHandle[ptypes.length];
1240             for (int i = 0; i < ptypes.length; i++) {
1241                 MethodHandle filter;
1242                 switch (mode) {
1243                     case SIZED:
1244                         // In sized mode, we convert all references and floats/doubles
1245                         // to String: there is no specialization for different
1246                         // classes in StringBuilder API, and it will convert to
1247                         // String internally anyhow.
1248                         filter = Stringifiers.forMost(ptypes[i]);
1249                         break;
1250                     case SIZED_EXACT:
1251                         // In exact mode, we convert everything to String:
1252                         // this helps to compute the storage exactly.
1253                         filter = Stringifiers.forAny(ptypes[i]);
1254                         break;
1255                     default:
1256                         throw new StringConcatException("Not supported");
1257                 }
1258                 if (filter != null) {
1259                     filters[i] = filter;
1260                     ptypes[i] = filter.type().returnType();
1261                 }
1262             }
1263 
1264             MethodHandle[] lengthers = new MethodHandle[pc];
1265 
1266             // Figure out lengths: constants' lengths can be deduced on the spot.
1267             // All reference arguments were filtered to String in the combinators below, so we can
1268             // call the usual String.length(). Primitive values string sizes can be estimated.
1269             int initial = 0;
1270             for (RecipeElement el : recipe.getElements()) {
1271                 switch (el.getTag()) {
1272                     case TAG_CONST:
1273                         Object cnst = el.getValue();
1274                         initial += cnst.toString().length();
1275                         break;
1276                     case TAG_ARG:
1277                         final int i = el.getArgPos();
1278                         Class<?> type = ptypes[i];
1279                         if (type.isPrimitive()) {
1280                             MethodHandle est = MethodHandles.constant(int.class, estimateSize(type));
1281                             est = MethodHandles.dropArguments(est, 0, type);
1282                             lengthers[i] = est;
1283                         } else {
1284                             lengthers[i] = STRING_LENGTH;
1285                         }
1286                         break;
1287                     default:
1288                         throw new StringConcatException("Unhandled tag: " + el.getTag());
1289                 }
1290             }
1291 
1292             // Create (StringBuilder, <args>) shape for appending:
1293             MethodHandle builder = MethodHandles.dropArguments(MethodHandles.identity(StringBuilder.class), 1, ptypes);
1294 
1295             // Compose append calls. This is done in reverse because the application order is
1296             // reverse as well.
1297             List<RecipeElement> elements = recipe.getElements();
1298             for (int i = elements.size() - 1; i >= 0; i--) {
1299                 RecipeElement el = elements.get(i);
1300                 MethodHandle appender;
1301                 switch (el.getTag()) {
1302                     case TAG_CONST:
1303                         Object constant = el.getValue();
1304                         MethodHandle mh = appender(adaptToStringBuilder(constant.getClass()));
1305                         appender = MethodHandles.insertArguments(mh, 1, constant);
1306                         break;
1307                     case TAG_ARG:
1308                         int ac = el.getArgPos();
1309                         appender = appender(ptypes[ac]);
1310 
1311                         // Insert dummy arguments to match the prefix in the signature.
1312                         // The actual appender argument will be the ac-ith argument.
1313                         if (ac != 0) {
1314                             appender = MethodHandles.dropArguments(appender, 1, Arrays.copyOf(ptypes, ac));
1315                         }
1316                         break;
1317                     default:
1318                         throw new StringConcatException("Unhandled tag: " + el.getTag());
1319                 }
1320                 builder = MethodHandles.foldArguments(builder, appender);
1321             }
1322 
1323             // Build the sub-tree that adds the sizes and produces a StringBuilder:
1324 
1325             // a) Start with the reducer that accepts all arguments, plus one
1326             //    slot for the initial value. Inject the initial value right away.
1327             //    This produces (<ints>)int shape:
1328             MethodHandle sum = getReducerFor(pc + 1);
1329             MethodHandle adder = MethodHandles.insertArguments(sum, 0, initial);
1330 
1331             // b) Apply lengthers to transform arguments to lengths, producing (<args>)int
1332             adder = MethodHandles.filterArguments(adder, 0, lengthers);
1333 
1334             // c) Instantiate StringBuilder (<args>)int -> (<args>)StringBuilder
1335             MethodHandle newBuilder = MethodHandles.filterReturnValue(adder, NEW_STRING_BUILDER);
1336 
1337             // d) Fold in StringBuilder constructor, this produces (<args>)StringBuilder
1338             MethodHandle mh = MethodHandles.foldArguments(builder, newBuilder);
1339 
1340             // Convert non-primitive arguments to Strings
1341             mh = MethodHandles.filterArguments(mh, 0, filters);
1342 
1343             // Convert (<args>)StringBuilder to (<args>)String
1344             if (DEBUG && mode.isExact()) {
1345                 mh = MethodHandles.filterReturnValue(mh, BUILDER_TO_STRING_CHECKED);
1346             } else {
1347                 mh = MethodHandles.filterReturnValue(mh, BUILDER_TO_STRING);
1348             }
1349 
1350             return mh;
1351         }
1352 
1353         private static MethodHandle getReducerFor(int cnt) {
1354             return SUMMERS.computeIfAbsent(cnt, SUMMER);
1355         }
1356 
1357         private static MethodHandle appender(Class<?> appendType) {
1358             MethodHandle appender = lookupVirtual(MethodHandles.publicLookup(), StringBuilder.class, "append",
1359                     StringBuilder.class, adaptToStringBuilder(appendType));
1360 
1361             // appenders should return void, this would not modify the target signature during folding
1362             MethodType nt = MethodType.methodType(void.class, StringBuilder.class, appendType);
1363             return appender.asType(nt);
1364         }
1365 
1366         private static String toStringChecked(StringBuilder sb) {
1367             String s = sb.toString();
1368             if (s.length() != sb.capacity()) {
1369                 throw new AssertionError("Exactness check failed: result length = " + s.length() + ", buffer capacity = " + sb.capacity());
1370             }
1371             return s;
1372         }
1373 
1374         private static int sum(int v1, int v2) {
1375             return v1 + v2;
1376         }
1377 
1378         private static int sum(int v1, int v2, int v3) {
1379             return v1 + v2 + v3;
1380         }
1381 
1382         private static int sum(int v1, int v2, int v3, int v4) {
1383             return v1 + v2 + v3 + v4;
1384         }
1385 
1386         private static int sum(int v1, int v2, int v3, int v4, int v5) {
1387             return v1 + v2 + v3 + v4 + v5;
1388         }
1389 
1390         private static int sum(int v1, int v2, int v3, int v4, int v5, int v6) {
1391             return v1 + v2 + v3 + v4 + v5 + v6;
1392         }
1393 
1394         private static int sum(int v1, int v2, int v3, int v4, int v5, int v6, int v7) {
1395             return v1 + v2 + v3 + v4 + v5 + v6 + v7;
1396         }
1397 
1398         private static int sum(int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8) {
1399             return v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8;
1400         }
1401 
1402         private static int sum(int initial, int[] vs) {
1403             int sum = initial;
1404             for (int v : vs) {
1405                 sum += v;
1406             }
1407             return sum;
1408         }
1409 
1410         private static final ConcurrentMap<Integer, MethodHandle> SUMMERS;
1411 
1412         // This one is deliberately non-lambdified to optimize startup time:
1413         private static final Function<Integer, MethodHandle> SUMMER = new Function<Integer, MethodHandle>() {
1414             @Override
1415             public MethodHandle apply(Integer cnt) {
1416                 if (cnt == 1) {
1417                     return MethodHandles.identity(int.class);
1418                 } else if (cnt <= 8) {
1419                     // Variable-arity collectors are not as efficient as small-count methods,
1420                     // unroll some initial sizes.
1421                     Class<?>[] cls = new Class<?>[cnt];
1422                     Arrays.fill(cls, int.class);
1423                     return lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, cls);
1424                 } else {
1425                     return lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, int.class, int[].class)
1426                             .asCollector(int[].class, cnt - 1);
1427                 }
1428             }
1429         };
1430 
1431         private static final MethodHandle NEW_STRING_BUILDER, STRING_LENGTH, BUILDER_TO_STRING, BUILDER_TO_STRING_CHECKED;
1432 
1433         static {
1434             SUMMERS = new ConcurrentHashMap<>();
1435             Lookup publicLookup = MethodHandles.publicLookup();
1436             NEW_STRING_BUILDER = lookupConstructor(publicLookup, StringBuilder.class, int.class);
1437             STRING_LENGTH = lookupVirtual(publicLookup, String.class, "length", int.class);
1438             BUILDER_TO_STRING = lookupVirtual(publicLookup, StringBuilder.class, "toString", String.class);
1439             if (DEBUG) {
1440                 BUILDER_TO_STRING_CHECKED = lookupStatic(MethodHandles.Lookup.IMPL_LOOKUP,
1441                         MethodHandleStringBuilderStrategy.class, "toStringChecked", String.class, StringBuilder.class);
1442             } else {
1443                 BUILDER_TO_STRING_CHECKED = null;
1444             }
1445         }
1446 
1447     }
1448 
1449 
1450     /**
1451      * <p><b>{@link Strategy#MH_INLINE_SIZED_EXACT}: "MethodHandles inline,
1452      * sized exactly".</b>
1453      *
1454      * <p>This strategy replicates what StringBuilders are doing: it builds the
1455      * byte[] array on its own and passes that byte[] array to String
1456      * constructor. This strategy requires access to some private APIs in JDK,
1457      * most notably, the read-only Integer/Long.stringSize methods that measure
1458      * the character length of the integers, and the private String constructor
1459      * that accepts byte[] arrays without copying. While this strategy assumes a
1460      * particular implementation details for String, this opens the door for
1461      * building a very optimal concatenation sequence. This is the only strategy
1462      * that requires porting if there are private JDK changes occur.
1463      */
1464     private static final class MethodHandleInlineCopyStrategy {
1465         static final Unsafe UNSAFE = Unsafe.getUnsafe();
1466 
1467         private MethodHandleInlineCopyStrategy() {
1468             // no instantiation
1469         }
1470 
1471         static MethodHandle generate(MethodType mt, Recipe recipe) throws Throwable {
1472 
1473             // Create filters and obtain filtered parameter types. Filters would be used in the beginning
1474             // to convert the incoming arguments into the arguments we can process (e.g. Objects -> Strings).
1475             // The filtered argument type list is used all over in the combinators below.
1476             Class<?>[] ptypes = mt.parameterArray();
1477             MethodHandle[] filters = null;
1478             for (int i = 0; i < ptypes.length; i++) {
1479                 MethodHandle filter = Stringifiers.forMost(ptypes[i]);
1480                 if (filter != null) {
1481                     if (filters == null) {
1482                         filters = new MethodHandle[ptypes.length];
1483                     }
1484                     filters[i] = filter;
1485                     ptypes[i] = filter.type().returnType();
1486                 }
1487             }
1488 
1489             // Start building the combinator tree. The tree "starts" with (<parameters>)String, and "finishes"
1490             // with the (int, byte[], byte)String in String helper. The combinators are assembled bottom-up,
1491             // which makes the code arguably hard to read.
1492 
1493             // Drop all remaining parameter types, leave only helper arguments:
1494             MethodHandle mh;
1495 
1496             mh = MethodHandles.dropArguments(NEW_STRING, 2, ptypes);
1497             mh = MethodHandles.dropArguments(mh, 1, int.class);
1498 
1499             // Safety: check that remaining index is zero -- that would mean the storage is completely
1500             // overwritten, and no leakage of uninitialized data occurred.
1501             mh = MethodHandles.filterArgument(mh, 1, CHECK_INDEX);
1502 
1503             // Mix in prependers. This happens when (byte[], int, byte) = (storage, index, coder) is already
1504             // known from the combinators below. We are assembling the string backwards, so "index" is the
1505             // *ending* index.
1506             for (RecipeElement el : recipe.getElements()) {
1507                 // Do the prepend, and put "new" index at index 1
1508                 mh = MethodHandles.dropArguments(mh, 2, int.class);
1509                 switch (el.getTag()) {
1510                     case TAG_CONST: {
1511                         Object cnst = el.getValue();
1512                         MethodHandle prepender = MethodHandles.insertArguments(prepender(cnst.getClass()), 3, cnst);
1513                         mh = MethodHandles.foldArguments(mh, 1, prepender,
1514                                 2, 0, 3 // index, storage, coder
1515                         );
1516                         break;
1517                     }
1518                     case TAG_ARG: {
1519                         int pos = el.getArgPos();
1520                         MethodHandle prepender = prepender(ptypes[pos]);
1521                         mh = MethodHandles.foldArguments(mh, 1, prepender,
1522                                 2, 0, 3, // index, storage, coder
1523                                 4 + pos  // selected argument
1524                         );
1525                         break;
1526                     }
1527                     default:
1528                         throw new StringConcatException("Unhandled tag: " + el.getTag());
1529                 }
















1530             }
1531 
1532             // Fold in byte[] instantiation at argument 0
1533             mh = MethodHandles.foldArguments(mh, 0, NEW_ARRAY,
1534                     1, 2 // index, coder
1535             );
1536 
1537             // Start combining length and coder mixers.
1538             //
1539             // Length is easy: constant lengths can be computed on the spot, and all non-constant
1540             // shapes have been either converted to Strings, or explicit methods for getting the
1541             // string length out of primitives are provided.
1542             //
1543             // Coders are more interesting. Only Object, String and char arguments (and constants)
1544             // can have non-Latin1 encoding. It is easier to blindly convert constants to String,
1545             // and deduce the coder from there. Arguments would be either converted to Strings
1546             // during the initial filtering, or handled by primitive specializations in CODER_MIXERS.
1547             //
1548             // The method handle shape after all length and coder mixers is:
1549             //   (int, byte, <args>)String = ("index", "coder", <args>)
1550             byte initialCoder = INITIAL_CODER;
1551             int initialLen = 0;    // initial length, in characters
1552             for (RecipeElement el : recipe.getElements()) {
1553                 switch (el.getTag()) {
1554                     case TAG_CONST:
1555                         Object constant = el.getValue();
1556                         String s = constant.toString();
1557                         initialCoder = (byte) coderMixer(String.class).invoke(initialCoder, s);
1558                         initialLen += s.length();
1559                         break;
1560                     case TAG_ARG:
1561                         int ac = el.getArgPos();
1562 
1563                         Class<?> argClass = ptypes[ac];
1564                         MethodHandle lm = lengthMixer(argClass);
1565                         MethodHandle cm = coderMixer(argClass);




1566 
1567                         // Read this bottom up:
1568 
1569                         // 4. Drop old index and coder, producing ("new-index", "new-coder", <args>)
1570                         mh = MethodHandles.dropArguments(mh, 2, int.class, byte.class);
1571 
1572                         // 3. Compute "new-index", producing ("new-index", "new-coder", "old-index", "old-coder", <args>)
1573                         //    Length mixer needs old index, plus the appropriate argument
1574                         mh = MethodHandles.foldArguments(mh, 0, lm,
1575                                 2, // old-index
1576                                 4 + ac // selected argument
1577                         );
1578 
1579                         // 2. Compute "new-coder", producing ("new-coder", "old-index", "old-coder", <args>)
1580                         //    Coder mixer needs old coder, plus the appropriate argument.
1581                         mh = MethodHandles.foldArguments(mh, 0, cm,
1582                                 2, // old-coder
1583                                 3 + ac // selected argument
1584                         );
1585 
1586                         // 1. The mh shape here is ("old-index", "old-coder", <args>)
1587                         break;
1588                     default:
1589                         throw new StringConcatException("Unhandled tag: " + el.getTag());
1590                 }
1591             }
1592 
1593             // Insert initial lengths and coders here.
1594             // The method handle shape here is (<args>).
1595             mh = MethodHandles.insertArguments(mh, 0, initialLen, initialCoder);
1596 
1597             // Apply filters, converting the arguments:
1598             if (filters != null) {
1599                 mh = MethodHandles.filterArguments(mh, 0, filters);
1600             }
1601 
1602             return mh;






















1603         }
1604 
1605         @ForceInline
1606         private static byte[] newArray(int length, byte coder) {
1607             return (byte[]) UNSAFE.allocateUninitializedArray(byte.class, length << coder);
1608         }
1609 
1610         @ForceInline
1611         private static int checkIndex(int index) {
1612             if (index != 0) {
1613                 throw new IllegalStateException("Storage is not completely initialized, " + index + " bytes left");
1614             }
1615             return index;
1616         }
1617 
1618         private static MethodHandle prepender(Class<?> cl) {
1619             return PREPENDERS.computeIfAbsent(cl, PREPEND);
1620         }
1621 
1622         private static MethodHandle coderMixer(Class<?> cl) {
1623             return CODER_MIXERS.computeIfAbsent(cl, CODER_MIX);
1624         }
1625 
1626         private static MethodHandle lengthMixer(Class<?> cl) {
1627             return LENGTH_MIXERS.computeIfAbsent(cl, LENGTH_MIX);
1628         }
1629 
1630         // This one is deliberately non-lambdified to optimize startup time:
1631         private static final Function<Class<?>, MethodHandle> PREPEND = new Function<Class<?>, MethodHandle>() {
1632             @Override
1633             public MethodHandle apply(Class<?> c) {
1634                 return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "prepend", int.class, int.class, byte[].class, byte.class, c);
1635             }
1636         };
1637 
1638         // This one is deliberately non-lambdified to optimize startup time:
1639         private static final Function<Class<?>, MethodHandle> CODER_MIX = new Function<Class<?>, MethodHandle>() {
1640             @Override
1641             public MethodHandle apply(Class<?> c) {
1642                 return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mixCoder", byte.class, byte.class, c);
1643             }
1644         };
1645 
1646         // This one is deliberately non-lambdified to optimize startup time:
1647         private static final Function<Class<?>, MethodHandle> LENGTH_MIX = new Function<Class<?>, MethodHandle>() {
1648             @Override
1649             public MethodHandle apply(Class<?> c) {
1650                 return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mixLen", int.class, int.class, c);
1651             }
1652         };
1653 
1654         private static final MethodHandle NEW_STRING;
1655         private static final MethodHandle CHECK_INDEX;
1656         private static final MethodHandle NEW_ARRAY;
1657         private static final ConcurrentMap<Class<?>, MethodHandle> PREPENDERS;
1658         private static final ConcurrentMap<Class<?>, MethodHandle> LENGTH_MIXERS;
1659         private static final ConcurrentMap<Class<?>, MethodHandle> CODER_MIXERS;
1660         private static final byte INITIAL_CODER;
1661         static final Class<?> STRING_HELPER;
1662 
1663         static {
1664             try {
1665                 STRING_HELPER = Class.forName("java.lang.StringConcatHelper");
1666                 MethodHandle initCoder = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "initialCoder", byte.class);
1667                 INITIAL_CODER = (byte) initCoder.invoke();
1668             } catch (Throwable e) {
1669                 throw new AssertionError(e);
1670             }
1671 
1672             PREPENDERS = new ConcurrentHashMap<>();
1673             LENGTH_MIXERS = new ConcurrentHashMap<>();
1674             CODER_MIXERS = new ConcurrentHashMap<>();
1675 
1676             NEW_STRING = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newString", String.class, byte[].class, byte.class);
1677             NEW_ARRAY  = lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleInlineCopyStrategy.class, "newArray", byte[].class, int.class, byte.class);
1678             CHECK_INDEX = lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleInlineCopyStrategy.class, "checkIndex", int.class, int.class);
1679         }
1680     }
1681 
1682     /**
1683      * Public gateways to public "stringify" methods. These methods have the form String apply(T obj), and normally
1684      * delegate to {@code String.valueOf}, depending on argument's type.
1685      */
1686     private static final class Stringifiers {
1687         private Stringifiers() {
1688             // no instantiation
1689         }
1690 
1691         private static class StringifierMost extends ClassValue<MethodHandle> {
1692             @Override
1693             protected MethodHandle computeValue(Class<?> cl) {
1694                 if (cl == String.class) {
1695                     return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, Object.class);
1696                 } else if (cl == float.class) {
1697                     return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, float.class);
1698                 } else if (cl == double.class) {
1699                     return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, double.class);
1700                 } else if (!cl.isPrimitive()) {
1701                     MethodHandle mhObject = lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, Object.class);
1702 
1703                     // We need the additional conversion here, because String.valueOf(Object) may return null.
1704                     // String conversion rules in Java state we need to produce "null" String in this case.
1705                     // It can be easily done with applying valueOf the second time.
1706                     return MethodHandles.filterReturnValue(mhObject,
1707                             mhObject.asType(MethodType.methodType(String.class, String.class)));
1708                 }
1709 
1710                 return null;
1711             }
1712         }
1713 
1714         private static class StringifierAny extends ClassValue<MethodHandle> {
1715             @Override
1716             protected MethodHandle computeValue(Class<?> cl) {
1717                 if (cl == byte.class || cl == short.class || cl == int.class) {
1718                     return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, int.class);
1719                 } else if (cl == boolean.class) {
1720                     return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, boolean.class);
1721                 } else if (cl == char.class) {
1722                     return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, char.class);
1723                 } else if (cl == long.class) {
1724                     return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, long.class);
1725                 } else {
1726                     MethodHandle mh = STRINGIFIERS_MOST.get(cl);
1727                     if (mh != null) {
1728                         return mh;
1729                     } else {
1730                         throw new IllegalStateException("Unknown class: " + cl);
1731                     }
1732                 }
1733             }
1734         }
1735 
1736         private static final ClassValue<MethodHandle> STRINGIFIERS_MOST = new StringifierMost();
1737         private static final ClassValue<MethodHandle> STRINGIFIERS_ANY = new StringifierAny();
1738 
1739         /**
1740          * Returns a stringifier for references and floats/doubles only.
1741          * Always returns null for other primitives.
1742          *
1743          * @param t class to stringify
1744          * @return stringifier; null, if not available
1745          */
1746         static MethodHandle forMost(Class<?> t) {
1747             return STRINGIFIERS_MOST.get(t);
1748         }
1749 
1750         /**
1751          * Returns a stringifier for any type. Never returns null.
1752          *
1753          * @param t class to stringify
1754          * @return stringifier
1755          */
1756         static MethodHandle forAny(Class<?> t) {
1757             return STRINGIFIERS_ANY.get(t);
1758         }
1759     }
1760 
1761     /* ------------------------------- Common utilities ------------------------------------ */
1762 
1763     static MethodHandle lookupStatic(Lookup lookup, Class<?> refc, String name, Class<?> rtype, Class<?>... ptypes) {
1764         try {
1765             return lookup.findStatic(refc, name, MethodType.methodType(rtype, ptypes));
1766         } catch (NoSuchMethodException | IllegalAccessException e) {
1767             throw new AssertionError(e);
1768         }
1769     }
1770 
1771     static MethodHandle lookupVirtual(Lookup lookup, Class<?> refc, String name, Class<?> rtype, Class<?>... ptypes) {
1772         try {
1773             return lookup.findVirtual(refc, name, MethodType.methodType(rtype, ptypes));
1774         } catch (NoSuchMethodException | IllegalAccessException e) {
1775             throw new AssertionError(e);
1776         }
1777     }
1778 
1779     static MethodHandle lookupConstructor(Lookup lookup, Class<?> refc, Class<?> ptypes) {
1780         try {
1781             return lookup.findConstructor(refc, MethodType.methodType(void.class, ptypes));
1782         } catch (NoSuchMethodException | IllegalAccessException e) {
1783             throw new AssertionError(e);
1784         }
1785     }
1786 
1787     static int estimateSize(Class<?> cl) {
1788         if (cl == Integer.TYPE) {
1789             return 11; // "-2147483648"
1790         } else if (cl == Boolean.TYPE) {
1791             return 5; // "false"
1792         } else if (cl == Byte.TYPE) {
1793             return 4; // "-128"
1794         } else if (cl == Character.TYPE) {
1795             return 1; // duh
1796         } else if (cl == Short.TYPE) {
1797             return 6; // "-32768"
1798         } else if (cl == Double.TYPE) {
1799             return 26; // apparently, no larger than this, see FloatingDecimal.BinaryToASCIIBuffer.buffer
1800         } else if (cl == Float.TYPE) {
1801             return 26; // apparently, no larger than this, see FloatingDecimal.BinaryToASCIIBuffer.buffer
1802         } else if (cl == Long.TYPE)  {
1803             return 20; // "-9223372036854775808"
1804         } else {
1805             throw new IllegalArgumentException("Cannot estimate the size for " + cl);
1806         }
1807     }
1808 
1809     static Class<?> adaptToStringBuilder(Class<?> c) {
1810         if (c.isPrimitive()) {
1811             if (c == Byte.TYPE || c == Short.TYPE) {
1812                 return int.class;
1813             }
1814         } else {
1815             if (c != String.class) {
1816                 return Object.class;
1817             }
1818         }
1819         return c;
1820     }
1821 
1822     private StringConcatFactory() {
1823         // no instantiation
1824     }
1825 
1826 }
--- EOF ---