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