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