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