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