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