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