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