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