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