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