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