1 /* 2 * Copyright (c) 2015, 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package org.graalvm.compiler.nodes.graphbuilderconf; 24 25 import static java.lang.String.format; 26 import static org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.LateClassPlugins.CLOSED_LATE_CLASS_PLUGIN; 27 28 import java.lang.reflect.Constructor; 29 import java.lang.reflect.Method; 30 import java.lang.reflect.Modifier; 31 import java.lang.reflect.Type; 32 import java.util.ArrayList; 33 import java.util.Arrays; 34 import java.util.Collections; 35 import java.util.List; 36 import java.util.Map; 37 38 import org.graalvm.compiler.api.replacements.MethodSubstitution; 39 import org.graalvm.compiler.api.replacements.MethodSubstitutionRegistry; 40 import org.graalvm.compiler.bytecode.BytecodeProvider; 41 import org.graalvm.compiler.debug.Assertions; 42 import org.graalvm.compiler.debug.GraalError; 43 import org.graalvm.compiler.graph.Node; 44 import org.graalvm.compiler.graph.iterators.NodeIterable; 45 import org.graalvm.compiler.nodes.ValueNode; 46 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin.Receiver; 47 import org.graalvm.util.EconomicMap; 48 import org.graalvm.util.Equivalence; 49 import org.graalvm.util.MapCursor; 50 import org.graalvm.util.Pair; 51 import org.graalvm.util.UnmodifiableEconomicMap; 52 import org.graalvm.util.UnmodifiableMapCursor; 53 54 import jdk.vm.ci.meta.MetaUtil; 55 import jdk.vm.ci.meta.ResolvedJavaMethod; 56 import jdk.vm.ci.meta.ResolvedJavaType; 57 import jdk.vm.ci.meta.Signature; 58 59 /** 60 * Manages a set of {@link InvocationPlugin}s. 61 * 62 * Most plugins are registered during initialization (i.e., before 63 * {@link #lookupInvocation(ResolvedJavaMethod)} or {@link #getBindings} is called). These 64 * registrations can be made with {@link Registration}, 65 * {@link #register(InvocationPlugin, String, String, Type...)}, 66 * {@link #register(InvocationPlugin, Type, String, Type...)} or 67 * {@link #registerOptional(InvocationPlugin, Type, String, Type...)}. Initialization is not 68 * thread-safe and so must only be performed by a single thread. 69 * 70 * Plugins that are not guaranteed to be made during initialization must use 71 * {@link LateRegistration}. 72 */ 73 public class InvocationPlugins { 74 75 public static class InvocationPluginReceiver implements InvocationPlugin.Receiver { 76 private final GraphBuilderContext parser; 77 private ValueNode[] args; 78 private ValueNode value; 79 80 public InvocationPluginReceiver(GraphBuilderContext parser) { 81 this.parser = parser; 82 } 83 84 @Override 85 public ValueNode get(boolean performNullCheck) { 86 assert args != null : "Cannot get the receiver of a static method"; 87 if (!performNullCheck) { 88 return args[0]; 89 } 90 if (value == null) { 91 value = parser.nullCheckedValue(args[0]); 92 if (value != args[0]) { 93 args[0] = value; 94 } 95 } 96 return value; 97 } 98 99 @Override 100 public boolean isConstant() { 101 return args[0].isConstant(); 102 } 103 104 public InvocationPluginReceiver init(ResolvedJavaMethod targetMethod, ValueNode[] newArgs) { 105 if (!targetMethod.isStatic()) { 106 this.args = newArgs; 107 this.value = null; 108 return this; 109 } 110 return null; 111 } 112 } 113 114 /** 115 * A symbol that is lazily {@linkplain OptionalLazySymbol#resolve() resolved} to a {@link Type}. 116 */ 117 static class OptionalLazySymbol implements Type { 118 private static final Class<?> MASK_NULL = OptionalLazySymbol.class; 119 private final String name; 120 private Class<?> resolved; 121 122 OptionalLazySymbol(String name) { 123 this.name = name; 124 } 125 126 @Override 127 public String getTypeName() { 128 return name; 129 } 130 131 /** 132 * Gets the resolved {@link Class} corresponding to this symbol or {@code null} if 133 * resolution fails. 134 */ 135 public Class<?> resolve() { 136 if (resolved == null) { 137 Class<?> resolvedOrNull = resolveClass(name, true); 138 resolved = resolvedOrNull == null ? MASK_NULL : resolvedOrNull; 139 } 140 return resolved == MASK_NULL ? null : resolved; 141 } 142 143 @Override 144 public String toString() { 145 return name; 146 } 147 } 148 149 /** 150 * Utility for {@linkplain InvocationPlugins#register(InvocationPlugin, Class, String, Class...) 151 * registration} of invocation plugins. 152 */ 153 public static class Registration implements MethodSubstitutionRegistry { 154 155 private final InvocationPlugins plugins; 156 private final Type declaringType; 157 private final BytecodeProvider methodSubstitutionBytecodeProvider; 158 private boolean allowOverwrite; 159 160 @Override 161 public Class<?> getReceiverType() { 162 return Receiver.class; 163 } 164 165 /** 166 * Creates an object for registering {@link InvocationPlugin}s for methods declared by a 167 * given class. 168 * 169 * @param plugins where to register the plugins 170 * @param declaringType the class declaring the methods for which plugins will be registered 171 * via this object 172 */ 173 public Registration(InvocationPlugins plugins, Type declaringType) { 174 this.plugins = plugins; 175 this.declaringType = declaringType; 176 this.methodSubstitutionBytecodeProvider = null; 177 } 178 179 /** 180 * Creates an object for registering {@link InvocationPlugin}s for methods declared by a 181 * given class. 182 * 183 * @param plugins where to register the plugins 184 * @param declaringType the class declaring the methods for which plugins will be registered 185 * via this object 186 * @param methodSubstitutionBytecodeProvider provider used to get the bytecodes to parse for 187 * method substitutions 188 */ 189 public Registration(InvocationPlugins plugins, Type declaringType, BytecodeProvider methodSubstitutionBytecodeProvider) { 190 this.plugins = plugins; 191 this.declaringType = declaringType; 192 this.methodSubstitutionBytecodeProvider = methodSubstitutionBytecodeProvider; 193 } 194 195 /** 196 * Creates an object for registering {@link InvocationPlugin}s for methods declared by a 197 * given class. 198 * 199 * @param plugins where to register the plugins 200 * @param declaringClassName the name of the class class declaring the methods for which 201 * plugins will be registered via this object 202 * @param methodSubstitutionBytecodeProvider provider used to get the bytecodes to parse for 203 * method substitutions 204 */ 205 public Registration(InvocationPlugins plugins, String declaringClassName, BytecodeProvider methodSubstitutionBytecodeProvider) { 206 this.plugins = plugins; 207 this.declaringType = new OptionalLazySymbol(declaringClassName); 208 this.methodSubstitutionBytecodeProvider = methodSubstitutionBytecodeProvider; 209 } 210 211 /** 212 * Configures this registration to allow or disallow overwriting of invocation plugins. 213 */ 214 public Registration setAllowOverwrite(boolean allowOverwrite) { 215 this.allowOverwrite = allowOverwrite; 216 return this; 217 } 218 219 /** 220 * Registers a plugin for a method with no arguments. 221 * 222 * @param name the name of the method 223 * @param plugin the plugin to be registered 224 */ 225 public void register0(String name, InvocationPlugin plugin) { 226 plugins.register(plugin, false, allowOverwrite, declaringType, name); 227 } 228 229 /** 230 * Registers a plugin for a method with 1 argument. 231 * 232 * @param name the name of the method 233 * @param plugin the plugin to be registered 234 */ 235 public void register1(String name, Type arg, InvocationPlugin plugin) { 236 plugins.register(plugin, false, allowOverwrite, declaringType, name, arg); 237 } 238 239 /** 240 * Registers a plugin for a method with 2 arguments. 241 * 242 * @param name the name of the method 243 * @param plugin the plugin to be registered 244 */ 245 public void register2(String name, Type arg1, Type arg2, InvocationPlugin plugin) { 246 plugins.register(plugin, false, allowOverwrite, declaringType, name, arg1, arg2); 247 } 248 249 /** 250 * Registers a plugin for a method with 3 arguments. 251 * 252 * @param name the name of the method 253 * @param plugin the plugin to be registered 254 */ 255 public void register3(String name, Type arg1, Type arg2, Type arg3, InvocationPlugin plugin) { 256 plugins.register(plugin, false, allowOverwrite, declaringType, name, arg1, arg2, arg3); 257 } 258 259 /** 260 * Registers a plugin for a method with 4 arguments. 261 * 262 * @param name the name of the method 263 * @param plugin the plugin to be registered 264 */ 265 public void register4(String name, Type arg1, Type arg2, Type arg3, Type arg4, InvocationPlugin plugin) { 266 plugins.register(plugin, false, allowOverwrite, declaringType, name, arg1, arg2, arg3, arg4); 267 } 268 269 /** 270 * Registers a plugin for a method with 5 arguments. 271 * 272 * @param name the name of the method 273 * @param plugin the plugin to be registered 274 */ 275 public void register5(String name, Type arg1, Type arg2, Type arg3, Type arg4, Type arg5, InvocationPlugin plugin) { 276 plugins.register(plugin, false, allowOverwrite, declaringType, name, arg1, arg2, arg3, arg4, arg5); 277 } 278 279 /** 280 * Registers a plugin for a method with 6 arguments. 281 * 282 * @param name the name of the method 283 * @param plugin the plugin to be registered 284 */ 285 public void register6(String name, Type arg1, Type arg2, Type arg3, Type arg4, Type arg5, Type arg6, InvocationPlugin plugin) { 286 plugins.register(plugin, false, allowOverwrite, declaringType, name, arg1, arg2, arg3, arg4, arg5, arg6); 287 } 288 289 /** 290 * Registers a plugin for a method with 7 arguments. 291 * 292 * @param name the name of the method 293 * @param plugin the plugin to be registered 294 */ 295 public void register7(String name, Type arg1, Type arg2, Type arg3, Type arg4, Type arg5, Type arg6, Type arg7, InvocationPlugin plugin) { 296 plugins.register(plugin, false, allowOverwrite, declaringType, name, arg1, arg2, arg3, arg4, arg5, arg6, arg7); 297 } 298 299 /** 300 * Registers a plugin for an optional method with no arguments. 301 * 302 * @param name the name of the method 303 * @param plugin the plugin to be registered 304 */ 305 public void registerOptional0(String name, InvocationPlugin plugin) { 306 plugins.register(plugin, true, allowOverwrite, declaringType, name); 307 } 308 309 /** 310 * Registers a plugin for an optional method with 1 argument. 311 * 312 * @param name the name of the method 313 * @param plugin the plugin to be registered 314 */ 315 public void registerOptional1(String name, Type arg, InvocationPlugin plugin) { 316 plugins.register(plugin, true, allowOverwrite, declaringType, name, arg); 317 } 318 319 /** 320 * Registers a plugin for an optional method with 2 arguments. 321 * 322 * @param name the name of the method 323 * @param plugin the plugin to be registered 324 */ 325 public void registerOptional2(String name, Type arg1, Type arg2, InvocationPlugin plugin) { 326 plugins.register(plugin, true, allowOverwrite, declaringType, name, arg1, arg2); 327 } 328 329 /** 330 * Registers a plugin for an optional method with 3 arguments. 331 * 332 * @param name the name of the method 333 * @param plugin the plugin to be registered 334 */ 335 public void registerOptional3(String name, Type arg1, Type arg2, Type arg3, InvocationPlugin plugin) { 336 plugins.register(plugin, true, allowOverwrite, declaringType, name, arg1, arg2, arg3); 337 } 338 339 /** 340 * Registers a plugin for an optional method with 4 arguments. 341 * 342 * @param name the name of the method 343 * @param plugin the plugin to be registered 344 */ 345 public void registerOptional4(String name, Type arg1, Type arg2, Type arg3, Type arg4, InvocationPlugin plugin) { 346 plugins.register(plugin, true, allowOverwrite, declaringType, name, arg1, arg2, arg3, arg4); 347 } 348 349 /** 350 * Registers a plugin that implements a method based on the bytecode of a substitute method. 351 * 352 * @param substituteDeclaringClass the class declaring the substitute method 353 * @param name the name of both the original and substitute method 354 * @param argumentTypes the argument types of the method. Element 0 of this array must be 355 * the {@link Class} value for {@link InvocationPlugin.Receiver} iff the method 356 * is non-static. Upon returning, element 0 will have been rewritten to 357 * {@code declaringClass} 358 */ 359 @Override 360 public void registerMethodSubstitution(Class<?> substituteDeclaringClass, String name, Type... argumentTypes) { 361 registerMethodSubstitution(substituteDeclaringClass, name, name, argumentTypes); 362 } 363 364 /** 365 * Registers a plugin that implements a method based on the bytecode of a substitute method. 366 * 367 * @param substituteDeclaringClass the class declaring the substitute method 368 * @param name the name of both the original method 369 * @param substituteName the name of the substitute method 370 * @param argumentTypes the argument types of the method. Element 0 of this array must be 371 * the {@link Class} value for {@link InvocationPlugin.Receiver} iff the method 372 * is non-static. Upon returning, element 0 will have been rewritten to 373 * {@code declaringClass} 374 */ 375 @Override 376 public void registerMethodSubstitution(Class<?> substituteDeclaringClass, String name, String substituteName, Type... argumentTypes) { 377 MethodSubstitutionPlugin plugin = createMethodSubstitution(substituteDeclaringClass, substituteName, argumentTypes); 378 plugins.register(plugin, false, allowOverwrite, declaringType, name, argumentTypes); 379 } 380 381 public MethodSubstitutionPlugin createMethodSubstitution(Class<?> substituteDeclaringClass, String substituteName, Type... argumentTypes) { 382 assert methodSubstitutionBytecodeProvider != null : "Registration used for method substitutions requires a non-null methodSubstitutionBytecodeProvider"; 383 MethodSubstitutionPlugin plugin = new MethodSubstitutionPlugin(methodSubstitutionBytecodeProvider, substituteDeclaringClass, substituteName, argumentTypes); 384 return plugin; 385 } 386 387 } 388 389 /** 390 * Utility for registering plugins after Graal may have been initialized. Registrations made via 391 * this class are not finalized until {@link #close} is called. 392 */ 393 public static class LateRegistration implements AutoCloseable { 394 395 private InvocationPlugins plugins; 396 private final List<Binding> bindings = new ArrayList<>(); 397 private final Type declaringType; 398 399 /** 400 * Creates an object for registering {@link InvocationPlugin}s for methods declared by a 401 * given class. 402 * 403 * @param plugins where to register the plugins 404 * @param declaringType the class declaring the methods for which plugins will be registered 405 * via this object 406 */ 407 public LateRegistration(InvocationPlugins plugins, Type declaringType) { 408 this.plugins = plugins; 409 this.declaringType = declaringType; 410 } 411 412 /** 413 * Registers an invocation plugin for a given method. There must be no plugin currently 414 * registered for {@code method}. 415 * 416 * @param argumentTypes the argument types of the method. Element 0 of this array must be 417 * the {@link Class} value for {@link InvocationPlugin.Receiver} iff the method 418 * is non-static. Upon returning, element 0 will have been rewritten to 419 * {@code declaringClass} 420 */ 421 public void register(InvocationPlugin plugin, String name, Type... argumentTypes) { 422 boolean isStatic = argumentTypes.length == 0 || argumentTypes[0] != InvocationPlugin.Receiver.class; 423 if (!isStatic) { 424 argumentTypes[0] = declaringType; 425 } 426 427 assert isStatic || argumentTypes[0] == declaringType; 428 Binding binding = new Binding(plugin, isStatic, name, argumentTypes); 429 bindings.add(binding); 430 431 assert Checks.check(this.plugins, declaringType, binding); 432 assert Checks.checkResolvable(false, declaringType, binding); 433 } 434 435 @Override 436 public void close() { 437 assert plugins != null : String.format("Late registrations of invocation plugins for %s is already closed", declaringType); 438 plugins.registerLate(declaringType, bindings); 439 plugins = null; 440 } 441 } 442 443 /** 444 * Associates an {@link InvocationPlugin} with the details of a method it substitutes. 445 */ 446 public static class Binding { 447 /** 448 * The plugin this binding is for. 449 */ 450 public final InvocationPlugin plugin; 451 452 /** 453 * Specifies if the associated method is static. 454 */ 455 public final boolean isStatic; 456 457 /** 458 * The name of the associated method. 459 */ 460 public final String name; 461 462 /** 463 * A partial 464 * <a href="http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3.3">method 465 * descriptor</a> for the associated method. The descriptor includes enclosing {@code '('} 466 * and {@code ')'} characters but omits the return type suffix. 467 */ 468 public final String argumentsDescriptor; 469 470 /** 471 * Link in a list of bindings. 472 */ 473 private Binding next; 474 475 Binding(InvocationPlugin data, boolean isStatic, String name, Type... argumentTypes) { 476 this.plugin = data; 477 this.isStatic = isStatic; 478 this.name = name; 479 StringBuilder buf = new StringBuilder(); 480 buf.append('('); 481 for (int i = isStatic ? 0 : 1; i < argumentTypes.length; i++) { 482 buf.append(MetaUtil.toInternalName(argumentTypes[i].getTypeName())); 483 } 484 buf.append(')'); 485 this.argumentsDescriptor = buf.toString(); 486 assert !name.equals("<init>") || !isStatic : this; 487 } 488 489 Binding(ResolvedJavaMethod resolved, InvocationPlugin data) { 490 this.plugin = data; 491 this.isStatic = resolved.isStatic(); 492 this.name = resolved.getName(); 493 Signature sig = resolved.getSignature(); 494 String desc = sig.toMethodDescriptor(); 495 assert desc.indexOf(')') != -1 : desc; 496 this.argumentsDescriptor = desc.substring(0, desc.indexOf(')') + 1); 497 assert !name.equals("<init>") || !isStatic : this; 498 } 499 500 @Override 501 public String toString() { 502 return name + argumentsDescriptor; 503 } 504 } 505 506 /** 507 * Plugin registrations for already resolved methods. If non-null, then {@link #registrations} 508 * is null and no further registrations can be made. 509 */ 510 private final UnmodifiableEconomicMap<ResolvedJavaMethod, InvocationPlugin> resolvedRegistrations; 511 512 /** 513 * Map from class names in {@linkplain MetaUtil#toInternalName(String) internal} form to the 514 * invocation plugin bindings for the class. Tf non-null, then {@link #resolvedRegistrations} 515 * will be null. 516 */ 517 private final EconomicMap<String, ClassPlugins> registrations; 518 519 /** 520 * Deferred registrations as well as the guard for delimiting the initial registration phase. 521 * The guard uses double-checked locking which is why this field is {@code volatile}. 522 */ 523 private volatile List<Runnable> deferredRegistrations = new ArrayList<>(); 524 525 /** 526 * Adds a {@link Runnable} for doing registration deferred until the first time 527 * {@link #get(ResolvedJavaMethod)} or {@link #closeRegistration()} is called on this object. 528 */ 529 public void defer(Runnable deferrable) { 530 assert deferredRegistrations != null : "registration is closed"; 531 deferredRegistrations.add(deferrable); 532 } 533 534 /** 535 * Support for registering plugins once this object may be accessed by multiple threads. 536 */ 537 private volatile LateClassPlugins lateRegistrations; 538 539 /** 540 * Per-class bindings. 541 */ 542 static class ClassPlugins { 543 544 /** 545 * Maps method names to binding lists. 546 */ 547 private final EconomicMap<String, Binding> bindings = EconomicMap.create(Equivalence.DEFAULT); 548 549 /** 550 * Gets the invocation plugin for a given method. 551 * 552 * @return the invocation plugin for {@code method} or {@code null} 553 */ 554 InvocationPlugin get(ResolvedJavaMethod method) { 555 assert !method.isBridge(); 556 Binding binding = bindings.get(method.getName()); 557 while (binding != null) { 558 if (method.isStatic() == binding.isStatic) { 559 if (method.getSignature().toMethodDescriptor().startsWith(binding.argumentsDescriptor)) { 560 return binding.plugin; 561 } 562 } 563 binding = binding.next; 564 } 565 return null; 566 } 567 568 public void register(Binding binding, boolean allowOverwrite) { 569 if (allowOverwrite) { 570 if (lookup(binding) != null) { 571 register(binding); 572 return; 573 } 574 } else { 575 assert lookup(binding) == null : "a value is already registered for " + binding; 576 } 577 register(binding); 578 } 579 580 InvocationPlugin lookup(Binding binding) { 581 Binding b = bindings.get(binding.name); 582 while (b != null) { 583 if (b.isStatic == binding.isStatic && b.argumentsDescriptor.equals(binding.argumentsDescriptor)) { 584 return b.plugin; 585 } 586 b = b.next; 587 } 588 return null; 589 } 590 591 /** 592 * Registers {@code binding}. 593 */ 594 void register(Binding binding) { 595 Binding head = bindings.get(binding.name); 596 assert binding.next == null; 597 binding.next = head; 598 bindings.put(binding.name, binding); 599 } 600 } 601 602 static class LateClassPlugins extends ClassPlugins { 603 static final String CLOSED_LATE_CLASS_PLUGIN = "-----"; 604 private final String className; 605 private final LateClassPlugins next; 606 607 LateClassPlugins(LateClassPlugins next, String className) { 608 assert next == null || next.className != CLOSED_LATE_CLASS_PLUGIN : "Late registration of invocation plugins is closed"; 609 this.next = next; 610 this.className = className; 611 } 612 } 613 614 /** 615 * Registers a binding of a method to an invocation plugin. 616 * 617 * @param plugin invocation plugin to be associated with the specified method 618 * @param isStatic specifies if the method is static 619 * @param declaringClass the class declaring the method 620 * @param name the name of the method 621 * @param argumentTypes the argument types of the method. Element 0 of this array must be 622 * {@code declaringClass} iff the method is non-static. 623 * @return an object representing the method 624 */ 625 Binding put(InvocationPlugin plugin, boolean isStatic, boolean allowOverwrite, Type declaringClass, String name, Type... argumentTypes) { 626 assert resolvedRegistrations == null : "registration is closed"; 627 String internalName = MetaUtil.toInternalName(declaringClass.getTypeName()); 628 assert isStatic || argumentTypes[0] == declaringClass; 629 assert deferredRegistrations != null : "initial registration is closed - use " + LateRegistration.class.getName() + " for late registrations"; 630 631 ClassPlugins classPlugins = registrations.get(internalName); 632 if (classPlugins == null) { 633 classPlugins = new ClassPlugins(); 634 registrations.put(internalName, classPlugins); 635 } 636 Binding binding = new Binding(plugin, isStatic, name, argumentTypes); 637 classPlugins.register(binding, allowOverwrite); 638 return binding; 639 } 640 641 InvocationPlugin get(ResolvedJavaMethod method) { 642 if (resolvedRegistrations != null) { 643 return resolvedRegistrations.get(method); 644 } else { 645 if (!method.isBridge()) { 646 ResolvedJavaType declaringClass = method.getDeclaringClass(); 647 flushDeferrables(); 648 String internalName = declaringClass.getName(); 649 ClassPlugins classPlugins = registrations.get(internalName); 650 InvocationPlugin res = null; 651 if (classPlugins != null) { 652 res = classPlugins.get(method); 653 } 654 if (res == null) { 655 LateClassPlugins lcp = findLateClassPlugins(internalName); 656 if (lcp != null) { 657 res = lcp.get(method); 658 } 659 } 660 if (res != null) { 661 if (canBeIntrinsified(declaringClass)) { 662 return res; 663 } 664 } 665 if (testExtensions != null) { 666 // Avoid the synchronization in the common case that there 667 // are no test extensions. 668 synchronized (this) { 669 if (testExtensions != null) { 670 List<Binding> bindings = testExtensions.get(internalName); 671 if (bindings != null) { 672 String name = method.getName(); 673 String descriptor = method.getSignature().toMethodDescriptor(); 674 for (Binding b : bindings) { 675 if (b.isStatic == method.isStatic() && 676 b.name.equals(name) && 677 descriptor.startsWith(b.argumentsDescriptor)) { 678 return b.plugin; 679 } 680 } 681 } 682 } 683 } 684 } 685 } else { 686 // Supporting plugins for bridge methods would require including 687 // the return type in the registered signature. Until needed, 688 // this extra complexity is best avoided. 689 } 690 } 691 return null; 692 } 693 694 /** 695 * Determines if methods in a given class can have invocation plugins. 696 * 697 * @param declaringClass the class to test 698 */ 699 protected boolean canBeIntrinsified(ResolvedJavaType declaringClass) { 700 return true; 701 } 702 703 LateClassPlugins findLateClassPlugins(String internalClassName) { 704 for (LateClassPlugins lcp = lateRegistrations; lcp != null; lcp = lcp.next) { 705 if (lcp.className.equals(internalClassName)) { 706 return lcp; 707 } 708 } 709 return null; 710 } 711 712 @SuppressWarnings("serial") 713 static class InvocationPlugRegistrationError extends GraalError { 714 InvocationPlugRegistrationError(Throwable cause) { 715 super(cause); 716 } 717 } 718 719 private void flushDeferrables() { 720 if (deferredRegistrations != null) { 721 synchronized (this) { 722 if (deferredRegistrations != null) { 723 try { 724 for (Runnable deferrable : deferredRegistrations) { 725 deferrable.run(); 726 } 727 deferredRegistrations = null; 728 } catch (InvocationPlugRegistrationError t) { 729 throw t; 730 } catch (Throwable t) { 731 /* 732 * Something went wrong during registration but it's possible we'll end up 733 * coming back into this code. nulling out deferredRegistrations would just 734 * cause other things to break and rerunning them would cause errors about 735 * already registered plugins, so rethrow the original exception during 736 * later invocations. 737 */ 738 deferredRegistrations.clear(); 739 Runnable rethrow = new Runnable() { 740 @Override 741 public void run() { 742 throw new InvocationPlugRegistrationError(t); 743 } 744 }; 745 deferredRegistrations.add(rethrow); 746 rethrow.run(); 747 } 748 } 749 } 750 } 751 } 752 753 private volatile EconomicMap<String, List<Binding>> testExtensions; 754 755 private static int findBinding(List<Binding> list, Binding key) { 756 for (int i = 0; i < list.size(); i++) { 757 Binding b = list.get(i); 758 if (b.isStatic == key.isStatic && b.name.equals(key.name) && b.argumentsDescriptor.equals(key.argumentsDescriptor)) { 759 return i; 760 } 761 } 762 return -1; 763 } 764 765 /** 766 * Extends the plugins in this object with those from {@code other}. The added plugins should be 767 * {@linkplain #removeTestPlugins(InvocationPlugins) removed} after the test. 768 * 769 * This extension mechanism exists only for tests that want to add extra invocation plugins 770 * after the compiler has been initialized. 771 * 772 * @param ignored if non-null, the bindings from {@code other} already in this object prior to 773 * calling this method are added to this list. These bindings are not added to this 774 * object. 775 */ 776 public synchronized void addTestPlugins(InvocationPlugins other, List<Pair<String, Binding>> ignored) { 777 assert resolvedRegistrations == null : "registration is closed"; 778 EconomicMap<String, List<Binding>> otherBindings = other.getBindings(true, false); 779 if (otherBindings.isEmpty()) { 780 return; 781 } 782 if (testExtensions == null) { 783 testExtensions = EconomicMap.create(); 784 } 785 MapCursor<String, List<Binding>> c = otherBindings.getEntries(); 786 while (c.advance()) { 787 String declaringClass = c.getKey(); 788 List<Binding> bindings = testExtensions.get(declaringClass); 789 if (bindings == null) { 790 bindings = new ArrayList<>(); 791 testExtensions.put(declaringClass, bindings); 792 } 793 for (Binding b : c.getValue()) { 794 int index = findBinding(bindings, b); 795 if (index != -1) { 796 if (ignored != null) { 797 ignored.add(Pair.create(declaringClass, b)); 798 } 799 } else { 800 bindings.add(b); 801 } 802 } 803 } 804 } 805 806 /** 807 * Removes the plugins from {@code other} in this object that were added by 808 * {@link #addTestPlugins}. 809 */ 810 public synchronized void removeTestPlugins(InvocationPlugins other) { 811 assert resolvedRegistrations == null : "registration is closed"; 812 if (testExtensions != null) { 813 MapCursor<String, List<Binding>> c = other.getBindings(false).getEntries(); 814 while (c.advance()) { 815 String declaringClass = c.getKey(); 816 List<Binding> bindings = testExtensions.get(declaringClass); 817 if (bindings != null) { 818 for (Binding b : c.getValue()) { 819 int index = findBinding(bindings, b); 820 if (index != -1) { 821 bindings.remove(index); 822 } 823 } 824 if (bindings.isEmpty()) { 825 testExtensions.removeKey(declaringClass); 826 } 827 } 828 } 829 if (testExtensions.isEmpty()) { 830 testExtensions = null; 831 } 832 } 833 } 834 835 synchronized void registerLate(Type declaringType, List<Binding> bindings) { 836 String internalName = MetaUtil.toInternalName(declaringType.getTypeName()); 837 assert findLateClassPlugins(internalName) == null : "Cannot have more than one late registration of invocation plugins for " + internalName; 838 LateClassPlugins lateClassPlugins = new LateClassPlugins(lateRegistrations, internalName); 839 for (Binding b : bindings) { 840 lateClassPlugins.register(b); 841 } 842 lateRegistrations = lateClassPlugins; 843 } 844 845 private synchronized boolean closeLateRegistrations() { 846 if (lateRegistrations == null || lateRegistrations.className != CLOSED_LATE_CLASS_PLUGIN) { 847 lateRegistrations = new LateClassPlugins(lateRegistrations, CLOSED_LATE_CLASS_PLUGIN); 848 } 849 return true; 850 } 851 852 /** 853 * Processes deferred registrations and then closes this object for future registration. 854 */ 855 public void closeRegistration() { 856 assert closeLateRegistrations(); 857 flushDeferrables(); 858 } 859 860 public boolean isEmpty() { 861 if (resolvedRegistrations != null) { 862 return resolvedRegistrations.isEmpty(); 863 } 864 return registrations.size() == 0 && lateRegistrations == null; 865 } 866 867 /** 868 * The plugins {@linkplain #lookupInvocation(ResolvedJavaMethod) searched} before searching in 869 * this object. 870 */ 871 protected final InvocationPlugins parent; 872 873 /** 874 * Creates a set of invocation plugins with no parent. 875 */ 876 public InvocationPlugins() { 877 this(null); 878 } 879 880 /** 881 * Creates a set of invocation plugins. 882 * 883 * @param parent if non-null, this object will be searched first when looking up plugins 884 */ 885 public InvocationPlugins(InvocationPlugins parent) { 886 InvocationPlugins p = parent; 887 this.parent = p; 888 this.registrations = EconomicMap.create(); 889 this.resolvedRegistrations = null; 890 } 891 892 /** 893 * Creates a closed set of invocation plugins for a set of resolved methods. Such an object 894 * cannot have further plugins registered. 895 */ 896 public InvocationPlugins(Map<ResolvedJavaMethod, InvocationPlugin> plugins, InvocationPlugins parent) { 897 this.parent = parent; 898 this.registrations = null; 899 this.deferredRegistrations = null; 900 EconomicMap<ResolvedJavaMethod, InvocationPlugin> map = EconomicMap.create(plugins.size()); 901 902 for (Map.Entry<ResolvedJavaMethod, InvocationPlugin> entry : plugins.entrySet()) { 903 map.put(entry.getKey(), entry.getValue()); 904 } 905 this.resolvedRegistrations = map; 906 } 907 908 protected void register(InvocationPlugin plugin, boolean isOptional, boolean allowOverwrite, Type declaringClass, String name, Type... argumentTypes) { 909 boolean isStatic = argumentTypes.length == 0 || argumentTypes[0] != InvocationPlugin.Receiver.class; 910 if (!isStatic) { 911 argumentTypes[0] = declaringClass; 912 } 913 Binding binding = put(plugin, isStatic, allowOverwrite, declaringClass, name, argumentTypes); 914 assert Checks.check(this, declaringClass, binding); 915 assert Checks.checkResolvable(isOptional, declaringClass, binding); 916 } 917 918 /** 919 * Registers an invocation plugin for a given method. There must be no plugin currently 920 * registered for {@code method}. 921 * 922 * @param argumentTypes the argument types of the method. Element 0 of this array must be the 923 * {@link Class} value for {@link InvocationPlugin.Receiver} iff the method is 924 * non-static. Upon returning, element 0 will have been rewritten to 925 * {@code declaringClass} 926 */ 927 public void register(InvocationPlugin plugin, Type declaringClass, String name, Type... argumentTypes) { 928 register(plugin, false, false, declaringClass, name, argumentTypes); 929 } 930 931 public void register(InvocationPlugin plugin, String declaringClass, String name, Type... argumentTypes) { 932 register(plugin, false, false, new OptionalLazySymbol(declaringClass), name, argumentTypes); 933 } 934 935 /** 936 * Registers an invocation plugin for a given, optional method. There must be no plugin 937 * currently registered for {@code method}. 938 * 939 * @param argumentTypes the argument types of the method. Element 0 of this array must be the 940 * {@link Class} value for {@link InvocationPlugin.Receiver} iff the method is 941 * non-static. Upon returning, element 0 will have been rewritten to 942 * {@code declaringClass} 943 */ 944 public void registerOptional(InvocationPlugin plugin, Type declaringClass, String name, Type... argumentTypes) { 945 register(plugin, true, false, declaringClass, name, argumentTypes); 946 } 947 948 /** 949 * Gets the plugin for a given method. 950 * 951 * @param method the method to lookup 952 * @return the plugin associated with {@code method} or {@code null} if none exists 953 */ 954 public InvocationPlugin lookupInvocation(ResolvedJavaMethod method) { 955 if (parent != null) { 956 InvocationPlugin plugin = parent.lookupInvocation(method); 957 if (plugin != null) { 958 return plugin; 959 } 960 } 961 return get(method); 962 } 963 964 /** 965 * Gets the set of registered invocation plugins. 966 * 967 * @return a map from class names in {@linkplain MetaUtil#toInternalName(String) internal} form 968 * to the invocation plugin bindings for methods in the class 969 */ 970 public EconomicMap<String, List<Binding>> getBindings(boolean includeParents) { 971 return getBindings(includeParents, true); 972 } 973 974 /** 975 * Gets the set of registered invocation plugins. 976 * 977 * @return a map from class names in {@linkplain MetaUtil#toInternalName(String) internal} form 978 * to the invocation plugin bindings for methods in the class 979 */ 980 private EconomicMap<String, List<Binding>> getBindings(boolean includeParents, boolean flushDeferrables) { 981 EconomicMap<String, List<Binding>> res = EconomicMap.create(Equivalence.DEFAULT); 982 if (parent != null && includeParents) { 983 res.putAll(parent.getBindings(true, flushDeferrables)); 984 } 985 if (resolvedRegistrations != null) { 986 UnmodifiableMapCursor<ResolvedJavaMethod, InvocationPlugin> cursor = resolvedRegistrations.getEntries(); 987 while (cursor.advance()) { 988 ResolvedJavaMethod method = cursor.getKey(); 989 InvocationPlugin plugin = cursor.getValue(); 990 String type = method.getDeclaringClass().getName(); 991 List<Binding> bindings = res.get(type); 992 if (bindings == null) { 993 bindings = new ArrayList<>(); 994 res.put(type, bindings); 995 } 996 bindings.add(new Binding(method, plugin)); 997 } 998 } else { 999 if (flushDeferrables) { 1000 flushDeferrables(); 1001 } 1002 MapCursor<String, ClassPlugins> classes = registrations.getEntries(); 1003 while (classes.advance()) { 1004 String type = classes.getKey(); 1005 ClassPlugins cp = classes.getValue(); 1006 collectBindingsTo(res, type, cp); 1007 } 1008 for (LateClassPlugins lcp = lateRegistrations; lcp != null; lcp = lcp.next) { 1009 String type = lcp.className; 1010 collectBindingsTo(res, type, lcp); 1011 } 1012 if (testExtensions != null) { 1013 // Avoid the synchronization in the common case that there 1014 // are no test extensions. 1015 synchronized (this) { 1016 if (testExtensions != null) { 1017 MapCursor<String, List<Binding>> c = testExtensions.getEntries(); 1018 while (c.advance()) { 1019 String name = c.getKey(); 1020 List<Binding> bindings = res.get(name); 1021 if (bindings == null) { 1022 bindings = new ArrayList<>(); 1023 res.put(name, bindings); 1024 } 1025 bindings.addAll(c.getValue()); 1026 } 1027 } 1028 } 1029 } 1030 } 1031 return res; 1032 } 1033 1034 private static void collectBindingsTo(EconomicMap<String, List<Binding>> res, String type, ClassPlugins cp) { 1035 MapCursor<String, Binding> methods = cp.bindings.getEntries(); 1036 while (methods.advance()) { 1037 List<Binding> bindings = res.get(type); 1038 if (bindings == null) { 1039 bindings = new ArrayList<>(); 1040 res.put(type, bindings); 1041 } 1042 for (Binding b = methods.getValue(); b != null; b = b.next) { 1043 bindings.add(b); 1044 } 1045 } 1046 } 1047 1048 /** 1049 * Gets the invocation plugins {@linkplain #lookupInvocation(ResolvedJavaMethod) searched} 1050 * before searching in this object. 1051 */ 1052 public InvocationPlugins getParent() { 1053 return parent; 1054 } 1055 1056 @Override 1057 public String toString() { 1058 UnmodifiableMapCursor<String, List<Binding>> entries = getBindings(false, false).getEntries(); 1059 List<String> all = new ArrayList<>(); 1060 while (entries.advance()) { 1061 String c = MetaUtil.internalNameToJava(entries.getKey(), true, false); 1062 for (Binding b : entries.getValue()) { 1063 all.add(c + '.' + b); 1064 } 1065 } 1066 Collections.sort(all); 1067 StringBuilder buf = new StringBuilder(); 1068 String nl = String.format("%n"); 1069 for (String s : all) { 1070 if (buf.length() != 0) { 1071 buf.append(nl); 1072 } 1073 buf.append(s); 1074 } 1075 if (parent != null) { 1076 if (buf.length() != 0) { 1077 buf.append(nl); 1078 } 1079 buf.append("// parent").append(nl).append(parent); 1080 } 1081 return buf.toString(); 1082 } 1083 1084 /** 1085 * Code only used in assertions. Putting this in a separate class reduces class load time. 1086 */ 1087 private static class Checks { 1088 private static final int MAX_ARITY = 7; 1089 /** 1090 * The set of all {@link InvocationPlugin#apply} method signatures. 1091 */ 1092 static final Class<?>[][] SIGS; 1093 1094 static { 1095 if (!Assertions.assertionsEnabled()) { 1096 throw new GraalError("%s must only be used in assertions", Checks.class.getName()); 1097 } 1098 ArrayList<Class<?>[]> sigs = new ArrayList<>(MAX_ARITY); 1099 for (Method method : InvocationPlugin.class.getDeclaredMethods()) { 1100 if (!Modifier.isStatic(method.getModifiers()) && method.getName().equals("apply")) { 1101 Class<?>[] sig = method.getParameterTypes(); 1102 assert sig[0] == GraphBuilderContext.class; 1103 assert sig[1] == ResolvedJavaMethod.class; 1104 assert sig[2] == InvocationPlugin.Receiver.class; 1105 assert Arrays.asList(sig).subList(3, sig.length).stream().allMatch(c -> c == ValueNode.class); 1106 while (sigs.size() < sig.length - 2) { 1107 sigs.add(null); 1108 } 1109 sigs.set(sig.length - 3, sig); 1110 } 1111 } 1112 assert sigs.indexOf(null) == -1 : format("need to add an apply() method to %s that takes %d %s arguments ", InvocationPlugin.class.getName(), sigs.indexOf(null), 1113 ValueNode.class.getSimpleName()); 1114 SIGS = sigs.toArray(new Class<?>[sigs.size()][]); 1115 } 1116 1117 static boolean containsBinding(InvocationPlugins p, Type declaringType, Binding key) { 1118 String internalName = MetaUtil.toInternalName(declaringType.getTypeName()); 1119 ClassPlugins classPlugins = p.registrations.get(internalName); 1120 return classPlugins != null && classPlugins.lookup(key) != null; 1121 } 1122 1123 public static boolean check(InvocationPlugins plugins, Type declaringType, Binding binding) { 1124 InvocationPlugin plugin = binding.plugin; 1125 InvocationPlugins p = plugins.parent; 1126 while (p != null) { 1127 assert !containsBinding(p, declaringType, binding) : "a plugin is already registered for " + binding; 1128 p = p.parent; 1129 } 1130 if (plugin instanceof ForeignCallPlugin || plugin instanceof GeneratedInvocationPlugin) { 1131 return true; 1132 } 1133 if (plugin instanceof MethodSubstitutionPlugin) { 1134 MethodSubstitutionPlugin msplugin = (MethodSubstitutionPlugin) plugin; 1135 Method substitute = msplugin.getJavaSubstitute(); 1136 assert substitute.getAnnotation(MethodSubstitution.class) != null : format("Substitute method must be annotated with @%s: %s", MethodSubstitution.class.getSimpleName(), substitute); 1137 return true; 1138 } 1139 int arguments = parseParameters(binding.argumentsDescriptor).size(); 1140 assert arguments < SIGS.length : format("need to extend %s to support method with %d arguments: %s", InvocationPlugin.class.getSimpleName(), arguments, binding); 1141 for (Method m : plugin.getClass().getDeclaredMethods()) { 1142 if (m.getName().equals("apply")) { 1143 Class<?>[] parameterTypes = m.getParameterTypes(); 1144 if (Arrays.equals(SIGS[arguments], parameterTypes)) { 1145 return true; 1146 } 1147 } 1148 } 1149 throw new AssertionError(format("graph builder plugin for %s not found", binding)); 1150 } 1151 1152 static boolean checkResolvable(boolean isOptional, Type declaringType, Binding binding) { 1153 Class<?> declaringClass = InvocationPlugins.resolveType(declaringType, isOptional); 1154 if (declaringClass == null) { 1155 return true; 1156 } 1157 if (binding.name.equals("<init>")) { 1158 if (resolveConstructor(declaringClass, binding) == null && !isOptional) { 1159 throw new AssertionError(String.format("Constructor not found: %s%s", declaringClass.getName(), binding.argumentsDescriptor)); 1160 } 1161 } else { 1162 if (resolveMethod(declaringClass, binding) == null && !isOptional) { 1163 throw new AssertionError(String.format("Method not found: %s.%s%s", declaringClass.getName(), binding.name, binding.argumentsDescriptor)); 1164 } 1165 } 1166 return true; 1167 } 1168 } 1169 1170 /** 1171 * Checks a set of nodes added to the graph by an {@link InvocationPlugin}. 1172 * 1173 * @param b the graph builder that applied the plugin 1174 * @param plugin a plugin that was just applied 1175 * @param newNodes the nodes added to the graph by {@code plugin} 1176 * @throws AssertionError if any check fail 1177 */ 1178 public void checkNewNodes(GraphBuilderContext b, InvocationPlugin plugin, NodeIterable<Node> newNodes) { 1179 if (parent != null) { 1180 parent.checkNewNodes(b, plugin, newNodes); 1181 } 1182 } 1183 1184 /** 1185 * Resolves a name to a class. 1186 * 1187 * @param className the name of the class to resolve 1188 * @param optional if true, resolution failure returns null 1189 * @return the resolved class or null if resolution fails and {@code optional} is true 1190 */ 1191 public static Class<?> resolveClass(String className, boolean optional) { 1192 try { 1193 // Need to use the system class loader to handle classes 1194 // loaded by the application class loader which is not 1195 // delegated to by the JVMCI class loader. 1196 ClassLoader cl = ClassLoader.getSystemClassLoader(); 1197 return Class.forName(className, false, cl); 1198 } catch (ClassNotFoundException e) { 1199 if (optional) { 1200 return null; 1201 } 1202 throw new GraalError("Could not resolve type " + className); 1203 } 1204 } 1205 1206 /** 1207 * Resolves a {@link Type} to a {@link Class}. 1208 * 1209 * @param type the type to resolve 1210 * @param optional if true, resolution failure returns null 1211 * @return the resolved class or null if resolution fails and {@code optional} is true 1212 */ 1213 public static Class<?> resolveType(Type type, boolean optional) { 1214 if (type instanceof Class) { 1215 return (Class<?>) type; 1216 } 1217 if (type instanceof OptionalLazySymbol) { 1218 return ((OptionalLazySymbol) type).resolve(); 1219 } 1220 return resolveClass(type.getTypeName(), optional); 1221 } 1222 1223 private static List<String> toInternalTypeNames(Class<?>[] types) { 1224 String[] res = new String[types.length]; 1225 for (int i = 0; i < types.length; i++) { 1226 res[i] = MetaUtil.toInternalName(types[i].getTypeName()); 1227 } 1228 return Arrays.asList(res); 1229 } 1230 1231 /** 1232 * Resolves a given binding to a method in a given class. If more than one method with the 1233 * parameter types matching {@code binding} is found and the return types of all the matching 1234 * methods form an inheritance chain, the one with the most specific type is returned; otherwise 1235 * {@link NoSuchMethodError} is thrown. 1236 * 1237 * @param declaringClass the class to search for a method matching {@code binding} 1238 * @return the method (if any) in {@code declaringClass} matching binding 1239 */ 1240 public static Method resolveMethod(Class<?> declaringClass, Binding binding) { 1241 if (binding.name.equals("<init>")) { 1242 return null; 1243 } 1244 Method[] methods = declaringClass.getDeclaredMethods(); 1245 List<String> parameterTypeNames = parseParameters(binding.argumentsDescriptor); 1246 for (int i = 0; i < methods.length; ++i) { 1247 Method m = methods[i]; 1248 if (binding.isStatic == Modifier.isStatic(m.getModifiers()) && m.getName().equals(binding.name)) { 1249 if (parameterTypeNames.equals(toInternalTypeNames(m.getParameterTypes()))) { 1250 for (int j = i + 1; j < methods.length; ++j) { 1251 Method other = methods[j]; 1252 if (binding.isStatic == Modifier.isStatic(other.getModifiers()) && other.getName().equals(binding.name)) { 1253 if (parameterTypeNames.equals(toInternalTypeNames(other.getParameterTypes()))) { 1254 if (m.getReturnType().isAssignableFrom(other.getReturnType())) { 1255 // `other` has a more specific return type - choose it 1256 // (m is most likely a bridge method) 1257 m = other; 1258 } else { 1259 if (!other.getReturnType().isAssignableFrom(m.getReturnType())) { 1260 throw new NoSuchMethodError(String.format( 1261 "Found 2 methods with same name and parameter types but unrelated return types:%n %s%n %s", m, other)); 1262 } 1263 } 1264 } 1265 } 1266 } 1267 return m; 1268 } 1269 } 1270 } 1271 return null; 1272 } 1273 1274 /** 1275 * Resolves a given binding to a constructor in a given class. 1276 * 1277 * @param declaringClass the class to search for a constructor matching {@code binding} 1278 * @return the constructor (if any) in {@code declaringClass} matching binding 1279 */ 1280 public static Constructor<?> resolveConstructor(Class<?> declaringClass, Binding binding) { 1281 if (!binding.name.equals("<init>")) { 1282 return null; 1283 } 1284 Constructor<?>[] constructors = declaringClass.getDeclaredConstructors(); 1285 List<String> parameterTypeNames = parseParameters(binding.argumentsDescriptor); 1286 for (int i = 0; i < constructors.length; ++i) { 1287 Constructor<?> c = constructors[i]; 1288 if (parameterTypeNames.equals(toInternalTypeNames(c.getParameterTypes()))) { 1289 return c; 1290 } 1291 } 1292 return null; 1293 } 1294 1295 private static List<String> parseParameters(String argumentsDescriptor) { 1296 assert argumentsDescriptor.startsWith("(") && argumentsDescriptor.endsWith(")") : argumentsDescriptor; 1297 List<String> res = new ArrayList<>(); 1298 int cur = 1; 1299 int end = argumentsDescriptor.length() - 1; 1300 while (cur != end) { 1301 char first; 1302 int start = cur; 1303 do { 1304 first = argumentsDescriptor.charAt(cur++); 1305 } while (first == '['); 1306 1307 switch (first) { 1308 case 'L': 1309 int endObject = argumentsDescriptor.indexOf(';', cur); 1310 if (endObject == -1) { 1311 throw new GraalError("Invalid object type at index %d in signature: %s", cur, argumentsDescriptor); 1312 } 1313 cur = endObject + 1; 1314 break; 1315 case 'V': 1316 case 'I': 1317 case 'B': 1318 case 'C': 1319 case 'D': 1320 case 'F': 1321 case 'J': 1322 case 'S': 1323 case 'Z': 1324 break; 1325 default: 1326 throw new GraalError("Invalid character at index %d in signature: %s", cur, argumentsDescriptor); 1327 } 1328 res.add(argumentsDescriptor.substring(start, cur)); 1329 } 1330 return res; 1331 } 1332 }