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

1509                 switch (el.getTag()) {
1510                     case TAG_CONST:
1511                         Object cnst = el.getValue();
1512                         prepender = MethodHandles.insertArguments(prepender(cnst.getClass()), 3, cnst);



1513                         break;
1514                     case TAG_ARG:

1515                         int pos = el.getArgPos();
1516                         prepender = selectArgument(prepender(ptypes[pos]), 3, ptypes, pos);




1517                         break;

1518                     default:
1519                         throw new StringConcatException("Unhandled tag: " + el.getTag());
1520                 }
1521 
1522                 // Remove "old" index from arguments
1523                 mh = MethodHandles.dropArguments(mh, 1, int.class);
1524 
1525                 // Do the prepend, and put "new" index at index 0
1526                 mh = MethodHandles.foldArguments(mh, prepender);
1527             }
1528 
1529             // Prepare the argument list for prepending. The tree below would instantiate
1530             // the storage byte[] into argument 0, so we need to swap "storage" and "index".
1531             // The index at this point equals to "size", and resides at argument 1.
1532             {
1533                 MethodType nmt = mh.type()
1534                         .changeParameterType(0, byte[].class)
1535                         .changeParameterType(1, int.class);
1536                 mh = MethodHandles.permuteArguments(mh, nmt, swap10(nmt.parameterCount()));
1537             }
1538 
1539             // Fold in byte[] instantiation at argument 0.
1540             MethodHandle combiner = MethodHandles.dropArguments(NEW_ARRAY, 2, ptypes);
1541             mh = MethodHandles.foldArguments(mh, combiner);

1542 
1543             // Start combining length and coder mixers.
1544             //
1545             // Length is easy: constant lengths can be computed on the spot, and all non-constant
1546             // shapes have been either converted to Strings, or explicit methods for getting the
1547             // string length out of primitives are provided.
1548             //
1549             // Coders are more interesting. Only Object, String and char arguments (and constants)
1550             // can have non-Latin1 encoding. It is easier to blindly convert constants to String,
1551             // and deduce the coder from there. Arguments would be either converted to Strings
1552             // during the initial filtering, or handled by primitive specializations in CODER_MIXERS.
1553             //
1554             // The method handle shape after all length and coder mixers is:
1555             //   (int, byte, <args>)String = ("index", "coder", <args>)
1556             byte initialCoder = INITIAL_CODER;
1557             int initialLen = 0;    // initial length, in characters
1558             for (RecipeElement el : recipe.getElements()) {
1559                 switch (el.getTag()) {
1560                     case TAG_CONST:
1561                         Object constant = el.getValue();
1562                         String s = constant.toString();
1563                         initialCoder = (byte) coderMixer(String.class).invoke(initialCoder, s);
1564                         initialLen += s.length();
1565                         break;
1566                     case TAG_ARG:
1567                         int ac = el.getArgPos();
1568 
1569                         Class<?> argClass = ptypes[ac];
1570                         MethodHandle lm = selectArgument(lengthMixer(argClass), 1, ptypes, ac);
1571                         lm = MethodHandles.dropArguments(lm, 0, byte.class); // (*)
1572                         lm = MethodHandles.dropArguments(lm, 2, byte.class);
1573 
1574                         MethodHandle cm = selectArgument(coderMixer(argClass),  1, ptypes, ac);
1575                         cm = MethodHandles.dropArguments(cm, 0, int.class);  // (**)
1576 
1577                         // Read this bottom up:
1578 
1579                         // 4. Drop old index and coder, producing ("new-index", "new-coder", <args>)
1580                         mh = MethodHandles.dropArguments(mh, 2, int.class, byte.class);
1581 
1582                         // 3. Compute "new-index", producing ("new-index", "new-coder", "old-index", "old-coder", <args>)
1583                         //    Length mixer ignores both "new-coder" and "old-coder" due to dropArguments above (*)
1584                         mh = MethodHandles.foldArguments(mh, lm);



1585 
1586                         // 2. Compute "new-coder", producing ("new-coder", "old-index", "old-coder", <args>)
1587                         //    Coder mixer ignores the "old-index" arg due to dropArguments above (**)
1588                         mh = MethodHandles.foldArguments(mh, cm);



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