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