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                 if (IS_IN_NATIVE_IMAGE && plugin instanceof MethodSubstitutionPlugin) {
1027                     // Disable method substitutions until GR-13607
1028                     return null;
1029                 }
1030                 return plugin;
1031             }
1032         }
1033         InvocationPlugin invocationPlugin = get(method);
1034         if (IS_IN_NATIVE_IMAGE && invocationPlugin instanceof MethodSubstitutionPlugin) {
1035             // Disable method substitutions until GR-13607
1036             return null;
1037         }
1038         return invocationPlugin;
1039     }
1040 
1041     /**
1042      * Gets the set of registered invocation plugins.
1043      *
1044      * @return a map from class names in {@linkplain MetaUtil#toInternalName(String) internal} form
1045      *         to the invocation plugin bindings for methods in the class
1046      */
1047     public EconomicMap<String, List<Binding>> getBindings(boolean includeParents) {
1048         return getBindings(includeParents, true);
1049     }
1050 
1051     /**
1052      * Gets the set of registered invocation plugins.
1053      *
1054      * @return a map from class names in {@linkplain MetaUtil#toInternalName(String) internal} form
1055      *         to the invocation plugin bindings for methods in the class
1056      */
1057     private EconomicMap<String, List<Binding>> getBindings(boolean includeParents, boolean flushDeferrables) {
1058         EconomicMap<String, List<Binding>> res = EconomicMap.create(Equivalence.DEFAULT);
1059         if (parent != null && includeParents) {
1060             res.putAll(parent.getBindings(true, flushDeferrables));
1061         }
1062         if (resolvedRegistrations != null) {
1063             UnmodifiableMapCursor<ResolvedJavaMethod, InvocationPlugin> cursor = resolvedRegistrations.getEntries();
1064             while (cursor.advance()) {
1065                 ResolvedJavaMethod method = cursor.getKey();
1066                 InvocationPlugin plugin = cursor.getValue();
1067                 String type = method.getDeclaringClass().getName();
1068                 List<Binding> bindings = res.get(type);
1069                 if (bindings == null) {
1070                     bindings = new ArrayList<>();
1071                     res.put(type, bindings);
1072                 }
1073                 bindings.add(new Binding(method, plugin));
1074             }
1075         } else {
1076             if (flushDeferrables) {
1077                 flushDeferrables();
1078             }
1079             MapCursor<String, ClassPlugins> classes = registrations.getEntries();
1080             while (classes.advance()) {
1081                 String type = classes.getKey();
1082                 ClassPlugins cp = classes.getValue();
1083                 collectBindingsTo(res, type, cp);
1084             }
1085             for (LateClassPlugins lcp = lateRegistrations; lcp != null; lcp = lcp.next) {
1086                 String type = lcp.className;
1087                 collectBindingsTo(res, type, lcp);
1088             }
1089             if (testExtensions != null) {
1090                 // Avoid the synchronization in the common case that there
1091                 // are no test extensions.
1092                 synchronized (this) {
1093                     if (testExtensions != null) {
1094                         MapCursor<String, List<Binding>> c = testExtensions.getEntries();
1095                         while (c.advance()) {
1096                             String name = c.getKey();
1097                             List<Binding> bindings = res.get(name);
1098                             if (bindings == null) {
1099                                 bindings = new ArrayList<>();
1100                                 res.put(name, bindings);
1101                             }
1102                             bindings.addAll(c.getValue());
1103                         }
1104                     }
1105                 }
1106             }
1107         }
1108         return res;
1109     }
1110 
1111     private static void collectBindingsTo(EconomicMap<String, List<Binding>> res, String type, ClassPlugins cp) {
1112         MapCursor<String, Binding> methods = cp.bindings.getEntries();
1113         while (methods.advance()) {
1114             List<Binding> bindings = res.get(type);
1115             if (bindings == null) {
1116                 bindings = new ArrayList<>();
1117                 res.put(type, bindings);
1118             }
1119             for (Binding b = methods.getValue(); b != null; b = b.next) {
1120                 bindings.add(b);
1121             }
1122         }
1123     }
1124 
1125     /**
1126      * Gets the invocation plugins {@linkplain #lookupInvocation(ResolvedJavaMethod) searched}
1127      * before searching in this object.
1128      */
1129     public InvocationPlugins getParent() {
1130         return parent;
1131     }
1132 
1133     @Override
1134     public String toString() {
1135         UnmodifiableMapCursor<String, List<Binding>> entries = getBindings(false, false).getEntries();
1136         List<String> all = new ArrayList<>();
1137         while (entries.advance()) {
1138             String c = MetaUtil.internalNameToJava(entries.getKey(), true, false);
1139             for (Binding b : entries.getValue()) {
1140                 all.add(c + '.' + b);
1141             }
1142         }
1143         Collections.sort(all);
1144         StringBuilder buf = new StringBuilder();
1145         String nl = String.format("%n");
1146         for (String s : all) {
1147             if (buf.length() != 0) {
1148                 buf.append(nl);
1149             }
1150             buf.append(s);
1151         }
1152         if (parent != null) {
1153             if (buf.length() != 0) {
1154                 buf.append(nl);
1155             }
1156             buf.append("// parent").append(nl).append(parent);
1157         }
1158         return buf.toString();
1159     }
1160 
1161     /**
1162      * Code only used in assertions. Putting this in a separate class reduces class load time.
1163      */
1164     private static class Checks {
1165         private static final int MAX_ARITY = 7;
1166         /**
1167          * The set of all {@link InvocationPlugin#apply} method signatures.
1168          */
1169         static final Class<?>[][] SIGS;
1170 
1171         static {
1172             if (!Assertions.assertionsEnabled() && !IS_BUILDING_NATIVE_IMAGE) {
1173                 throw new GraalError("%s must only be used in assertions", Checks.class.getName());
1174             }
1175             ArrayList<Class<?>[]> sigs = new ArrayList<>(MAX_ARITY);
1176             if (!IS_IN_NATIVE_IMAGE) {
1177                 for (Method method : InvocationPlugin.class.getDeclaredMethods()) {
1178                     if (!Modifier.isStatic(method.getModifiers()) && method.getName().equals("apply")) {
1179                         Class<?>[] sig = method.getParameterTypes();
1180                         assert sig[0] == GraphBuilderContext.class;
1181                         assert sig[1] == ResolvedJavaMethod.class;
1182                         assert sig[2] == InvocationPlugin.Receiver.class;
1183                         assert Arrays.asList(sig).subList(3, sig.length).stream().allMatch(c -> c == ValueNode.class);
1184                         while (sigs.size() < sig.length - 2) {
1185                             sigs.add(null);
1186                         }
1187                         sigs.set(sig.length - 3, sig);
1188                     }
1189                 }
1190                 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),
1191                                 ValueNode.class.getSimpleName());
1192             }
1193             SIGS = sigs.toArray(new Class<?>[sigs.size()][]);
1194         }
1195 
1196         static boolean containsBinding(InvocationPlugins p, Type declaringType, Binding key) {
1197             String internalName = MetaUtil.toInternalName(declaringType.getTypeName());
1198             ClassPlugins classPlugins = p.registrations.get(internalName);
1199             return classPlugins != null && classPlugins.lookup(key) != null;
1200         }
1201 
1202         public static boolean check(InvocationPlugins plugins, Type declaringType, Binding binding) {
1203             InvocationPlugin plugin = binding.plugin;
1204             InvocationPlugins p = plugins.parent;
1205             while (p != null) {
1206                 assert !containsBinding(p, declaringType, binding) : "a plugin is already registered for " + binding;
1207                 p = p.parent;
1208             }
1209             if (plugin instanceof ForeignCallPlugin || plugin instanceof GeneratedInvocationPlugin) {
1210                 return true;
1211             }
1212             if (plugin instanceof MethodSubstitutionPlugin) {
1213                 MethodSubstitutionPlugin msplugin = (MethodSubstitutionPlugin) plugin;
1214                 Method substitute = msplugin.getJavaSubstitute();
1215                 assert substitute.getAnnotation(MethodSubstitution.class) != null : format("Substitute method must be annotated with @%s: %s", MethodSubstitution.class.getSimpleName(), substitute);
1216                 return true;
1217             }
1218             int arguments = parseParameters(binding.argumentsDescriptor).size();
1219             assert arguments < SIGS.length : format("need to extend %s to support method with %d arguments: %s", InvocationPlugin.class.getSimpleName(), arguments, binding);
1220             for (Method m : plugin.getClass().getDeclaredMethods()) {
1221                 if (m.getName().equals("apply")) {
1222                     Class<?>[] parameterTypes = m.getParameterTypes();
1223                     if (Arrays.equals(SIGS[arguments], parameterTypes)) {
1224                         return true;
1225                     }
1226                 }
1227             }
1228             throw new AssertionError(format("graph builder plugin for %s not found", binding));
1229         }
1230 
1231         static boolean checkResolvable(boolean isOptional, Type declaringType, Binding binding) {
1232             if (declaringType instanceof ResolvedJavaSymbol) {
1233                 return checkResolvable(isOptional, ((ResolvedJavaSymbol) declaringType).getResolved(), binding);
1234             }
1235             Class<?> declaringClass = InvocationPlugins.resolveType(declaringType, isOptional);
1236             if (declaringClass == null) {
1237                 return true;
1238             }
1239             if (binding.name.equals("<init>")) {
1240                 if (resolveConstructor(declaringClass, binding) == null && !isOptional) {
1241                     throw new AssertionError(String.format("Constructor not found: %s%s", declaringClass.getName(), binding.argumentsDescriptor));
1242                 }
1243             } else {
1244                 if (resolveMethod(declaringClass, binding) == null && !isOptional) {
1245                     throw new AssertionError(String.format("Method not found: %s.%s%s", declaringClass.getName(), binding.name, binding.argumentsDescriptor));
1246                 }
1247             }
1248             return true;
1249         }
1250 
1251         private static boolean checkResolvable(boolean isOptional, ResolvedJavaType declaringType, Binding binding) {
1252             if (resolveJavaMethod(declaringType, binding) == null && !isOptional) {
1253                 throw new AssertionError(String.format("Method not found: %s.%s%s", declaringType.toJavaName(), binding.name, binding.argumentsDescriptor));
1254             }
1255             return true;
1256         }
1257     }
1258 
1259     /**
1260      * Checks a set of nodes added to the graph by an {@link InvocationPlugin}.
1261      *
1262      * @param b the graph builder that applied the plugin
1263      * @param plugin a plugin that was just applied
1264      * @param newNodes the nodes added to the graph by {@code plugin}
1265      * @throws AssertionError if any check fail
1266      */
1267     public void checkNewNodes(GraphBuilderContext b, InvocationPlugin plugin, NodeIterable<Node> newNodes) {
1268         if (parent != null) {
1269             parent.checkNewNodes(b, plugin, newNodes);
1270         }
1271     }
1272 
1273     /**
1274      * Resolves a name to a class.
1275      *
1276      * @param className the name of the class to resolve
1277      * @param optional if true, resolution failure returns null
1278      * @return the resolved class or null if resolution fails and {@code optional} is true
1279      */
1280     public static Class<?> resolveClass(String className, boolean optional) {
1281         try {
1282             // Need to use the system class loader to handle classes
1283             // loaded by the application class loader which is not
1284             // delegated to by the JVMCI class loader.
1285             ClassLoader cl = ClassLoader.getSystemClassLoader();
1286             return Class.forName(className, false, cl);
1287         } catch (ClassNotFoundException e) {
1288             if (optional) {
1289                 return null;
1290             }
1291             throw new GraalError("Could not resolve type " + className);
1292         }
1293     }
1294 
1295     /**
1296      * Resolves a {@link Type} to a {@link Class}.
1297      *
1298      * @param type the type to resolve
1299      * @param optional if true, resolution failure returns null
1300      * @return the resolved class or null if resolution fails and {@code optional} is true
1301      */
1302     public static Class<?> resolveType(Type type, boolean optional) {
1303         if (type instanceof Class) {
1304             return (Class<?>) type;
1305         }
1306         if (type instanceof OptionalLazySymbol) {
1307             return ((OptionalLazySymbol) type).resolve();
1308         }
1309         if (IS_IN_NATIVE_IMAGE) {
1310             throw new GraalError("Unresolved type in native image image:" + type.getTypeName());
1311         }
1312         return resolveClass(type.getTypeName(), optional);
1313     }
1314 
1315     private static List<String> toInternalTypeNames(Class<?>[] types) {
1316         String[] res = new String[types.length];
1317         for (int i = 0; i < types.length; i++) {
1318             res[i] = MetaUtil.toInternalName(types[i].getTypeName());
1319         }
1320         return Arrays.asList(res);
1321     }
1322 
1323     /**
1324      * Resolves a given binding to a method in a given class. If more than one method with the
1325      * parameter types matching {@code binding} is found and the return types of all the matching
1326      * methods form an inheritance chain, the one with the most specific type is returned; otherwise
1327      * {@link NoSuchMethodError} is thrown.
1328      *
1329      * @param declaringClass the class to search for a method matching {@code binding}
1330      * @return the method (if any) in {@code declaringClass} matching {@code binding}
1331      */
1332     public static Method resolveMethod(Class<?> declaringClass, Binding binding) {
1333         if (binding.name.equals("<init>")) {
1334             return null;
1335         }
1336         Method[] methods = declaringClass.getDeclaredMethods();
1337         List<String> parameterTypeNames = parseParameters(binding.argumentsDescriptor);
1338         Method match = null;
1339         for (int i = 0; i < methods.length; ++i) {
1340             Method m = methods[i];
1341             if (binding.isStatic == Modifier.isStatic(m.getModifiers()) &&
1342                             m.getName().equals(binding.name) &&
1343                             parameterTypeNames.equals(toInternalTypeNames(m.getParameterTypes()))) {
1344                 if (match == null) {
1345                     match = m;
1346                 } else if (match.getReturnType().isAssignableFrom(m.getReturnType())) {
1347                     // `m` has a more specific return type - choose it
1348                     // (`match` is most likely a bridge method)
1349                     match = m;
1350                 } else {
1351                     if (!m.getReturnType().isAssignableFrom(match.getReturnType())) {
1352                         throw new NoSuchMethodError(String.format(
1353                                         "Found 2 methods with same name and parameter types but unrelated return types:%n %s%n %s", match, m));
1354                     }
1355                 }
1356             }
1357         }
1358         return match;
1359     }
1360 
1361     /**
1362      * Same as {@link #resolveMethod(Class, Binding)} and
1363      * {@link #resolveConstructor(Class, Binding)} except in terms of {@link ResolvedJavaType} and
1364      * {@link ResolvedJavaMethod}.
1365      */
1366     public static ResolvedJavaMethod resolveJavaMethod(ResolvedJavaType declaringClass, Binding binding) {
1367         ResolvedJavaMethod[] methods = declaringClass.getDeclaredMethods();
1368         if (binding.name.equals("<init>")) {
1369             for (ResolvedJavaMethod m : methods) {
1370                 if (m.getName().equals("<init>") && m.getSignature().toMethodDescriptor().startsWith(binding.argumentsDescriptor)) {
1371                     return m;
1372                 }
1373             }
1374             return null;
1375         }
1376 
1377         ResolvedJavaMethod match = null;
1378         for (int i = 0; i < methods.length; ++i) {
1379             ResolvedJavaMethod m = methods[i];
1380             if (binding.isStatic == m.isStatic() &&
1381                             m.getName().equals(binding.name) &&
1382                             m.getSignature().toMethodDescriptor().startsWith(binding.argumentsDescriptor)) {
1383                 if (match == null) {
1384                     match = m;
1385                 } else {
1386                     final ResolvedJavaType matchReturnType = (ResolvedJavaType) match.getSignature().getReturnType(declaringClass);
1387                     final ResolvedJavaType mReturnType = (ResolvedJavaType) m.getSignature().getReturnType(declaringClass);
1388                     if (matchReturnType.isAssignableFrom(mReturnType)) {
1389                         // `m` has a more specific return type - choose it
1390                         // (`match` is most likely a bridge method)
1391                         match = m;
1392                     } else {
1393                         if (!mReturnType.isAssignableFrom(matchReturnType)) {
1394                             throw new NoSuchMethodError(String.format(
1395                                             "Found 2 methods with same name and parameter types but unrelated return types:%n %s%n %s", match, m));
1396                         }
1397                     }
1398                 }
1399             }
1400         }
1401         return match;
1402     }
1403 
1404     /**
1405      * Resolves a given binding to a constructor in a given class.
1406      *
1407      * @param declaringClass the class to search for a constructor matching {@code binding}
1408      * @return the constructor (if any) in {@code declaringClass} matching binding
1409      */
1410     public static Constructor<?> resolveConstructor(Class<?> declaringClass, Binding binding) {
1411         if (!binding.name.equals("<init>")) {
1412             return null;
1413         }
1414         Constructor<?>[] constructors = declaringClass.getDeclaredConstructors();
1415         List<String> parameterTypeNames = parseParameters(binding.argumentsDescriptor);
1416         for (int i = 0; i < constructors.length; ++i) {
1417             Constructor<?> c = constructors[i];
1418             if (parameterTypeNames.equals(toInternalTypeNames(c.getParameterTypes()))) {
1419                 return c;
1420             }
1421         }
1422         return null;
1423     }
1424 
1425     private static List<String> parseParameters(String argumentsDescriptor) {
1426         assert argumentsDescriptor.startsWith("(") && argumentsDescriptor.endsWith(")") : argumentsDescriptor;
1427         List<String> res = new ArrayList<>();
1428         int cur = 1;
1429         int end = argumentsDescriptor.length() - 1;
1430         while (cur != end) {
1431             char first;
1432             int start = cur;
1433             do {
1434                 first = argumentsDescriptor.charAt(cur++);
1435             } while (first == '[');
1436 
1437             switch (first) {
1438                 case 'L':
1439                     int endObject = argumentsDescriptor.indexOf(';', cur);
1440                     if (endObject == -1) {
1441                         throw new GraalError("Invalid object type at index %d in signature: %s", cur, argumentsDescriptor);
1442                     }
1443                     cur = endObject + 1;
1444                     break;
1445                 case 'V':
1446                 case 'I':
1447                 case 'B':
1448                 case 'C':
1449                 case 'D':
1450                 case 'F':
1451                 case 'J':
1452                 case 'S':
1453                 case 'Z':
1454                     break;
1455                 default:
1456                     throw new GraalError("Invalid character at index %d in signature: %s", cur, argumentsDescriptor);
1457             }
1458             res.add(argumentsDescriptor.substring(start, cur));
1459         }
1460         return res;
1461     }
1462 }