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