< prev index next >

src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/InvocationPlugins.java

Print this page

        

*** 21,59 **** * questions. */ package org.graalvm.compiler.nodes.graphbuilderconf; import static java.lang.String.format; ! import java.lang.reflect.Executable; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; - import java.util.HashSet; import java.util.List; import java.util.Map; - import java.util.Set; import org.graalvm.compiler.api.replacements.MethodSubstitution; import org.graalvm.compiler.api.replacements.MethodSubstitutionRegistry; import org.graalvm.compiler.bytecode.BytecodeProvider; import org.graalvm.compiler.debug.GraalError; import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.graph.iterators.NodeIterable; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin.Receiver; - import jdk.vm.ci.meta.MetaAccessProvider; import jdk.vm.ci.meta.MetaUtil; import jdk.vm.ci.meta.ResolvedJavaMethod; /** * Manages a set of {@link InvocationPlugin}s. */ public class InvocationPlugins { public static class InvocationPluginReceiver implements InvocationPlugin.Receiver { private final GraphBuilderContext parser; --- 21,70 ---- * questions. */ package org.graalvm.compiler.nodes.graphbuilderconf; import static java.lang.String.format; + import static org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.LateClassPlugins.CLOSED_LATE_CLASS_PLUGIN; ! import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.graalvm.compiler.api.replacements.MethodSubstitution; import org.graalvm.compiler.api.replacements.MethodSubstitutionRegistry; import org.graalvm.compiler.bytecode.BytecodeProvider; import org.graalvm.compiler.debug.GraalError; import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.graph.iterators.NodeIterable; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin.Receiver; import jdk.vm.ci.meta.MetaUtil; import jdk.vm.ci.meta.ResolvedJavaMethod; + import jdk.vm.ci.meta.ResolvedJavaType; + import jdk.vm.ci.meta.Signature; /** * Manages a set of {@link InvocationPlugin}s. + * + * Most plugins are registered during initialization (i.e., before + * {@link #lookupInvocation(ResolvedJavaMethod)} or {@link #getBindings} is called). These + * registrations can be made with {@link Registration}, + * {@link #register(InvocationPlugin, String, String, Type...)}, + * {@link #register(InvocationPlugin, Type, String, Type...)} or + * {@link #registerOptional(InvocationPlugin, Type, String, Type...)}. Initialization is not + * thread-safe and so must only be performed by a single thread. + * + * Plugins that are not guaranteed to be made during initialization must use + * {@link LateRegistration}. */ public class InvocationPlugins { public static class InvocationPluginReceiver implements InvocationPlugin.Receiver { private final GraphBuilderContext parser;
*** 258,267 **** --- 269,298 ---- public void register5(String name, Type arg1, Type arg2, Type arg3, Type arg4, Type arg5, InvocationPlugin plugin) { plugins.register(plugin, false, allowOverwrite, declaringType, name, arg1, arg2, arg3, arg4, arg5); } /** + * Registers a plugin for a method with 6 arguments. + * + * @param name the name of the method + * @param plugin the plugin to be registered + */ + public void register6(String name, Type arg1, Type arg2, Type arg3, Type arg4, Type arg5, Type arg6, InvocationPlugin plugin) { + plugins.register(plugin, false, allowOverwrite, declaringType, name, arg1, arg2, arg3, arg4, arg5, arg6); + } + + /** + * Registers a plugin for a method with 7 arguments. + * + * @param name the name of the method + * @param plugin the plugin to be registered + */ + public void register7(String name, Type arg1, Type arg2, Type arg3, Type arg4, Type arg5, Type arg6, Type arg7, InvocationPlugin plugin) { + plugins.register(plugin, false, allowOverwrite, declaringType, name, arg1, arg2, arg3, arg4, arg5, arg6, arg7); + } + + /** * Registers a plugin for an optional method with no arguments. * * @param name the name of the method * @param plugin the plugin to be registered */
*** 335,506 **** * is non-static. Upon returning, element 0 will have been rewritten to * {@code declaringClass} */ @Override public void registerMethodSubstitution(Class<?> substituteDeclaringClass, String name, String substituteName, Type... argumentTypes) { assert methodSubstitutionBytecodeProvider != null : "Registration used for method substitutions requires a non-null methodSubstitutionBytecodeProvider"; MethodSubstitutionPlugin plugin = new MethodSubstitutionPlugin(methodSubstitutionBytecodeProvider, substituteDeclaringClass, substituteName, argumentTypes); ! plugins.register(plugin, false, allowOverwrite, declaringType, name, argumentTypes); } } /** ! * Key for a {@linkplain ClassPlugins#entries resolved} plugin registration. Due to the ! * possibility of class redefinition, we cannot directly use {@link ResolvedJavaMethod}s as ! * keys. A {@link ResolvedJavaMethod} implementation might implement {@code equals()} and ! * {@code hashCode()} based on internal representation subject to change by class redefinition. */ ! static final class ResolvedJavaMethodKey { ! private final ResolvedJavaMethod method; ! ResolvedJavaMethodKey(ResolvedJavaMethod method) { ! this.method = method; ! } ! @Override ! public boolean equals(Object obj) { ! if (obj instanceof ResolvedJavaMethodKey) { ! ResolvedJavaMethodKey that = (ResolvedJavaMethodKey) obj; ! if (this.method.isStatic() == that.method.isStatic()) { ! if (this.method.getDeclaringClass().equals(that.method.getDeclaringClass())) { ! if (this.method.getName().equals(that.method.getName())) { ! if (this.method.getSignature().equals(that.method.getSignature())) { ! return true; ! } ! } ! } ! } } ! return false; } ! @Override ! public int hashCode() { ! return this.method.getName().hashCode(); } @Override ! public String toString() { ! return "ResolvedJavaMethodKey<" + method + ">"; } } /** ! * Key for {@linkplain ClassPlugins#registrations registering} an {@link InvocationPlugin} for a ! * specific method. */ ! static class MethodKey { ! final boolean isStatic; /** ! * This method is optional. This is used for new API methods not present in previous JDK ! * versions. */ ! final boolean isOptional; ! final String name; ! final Type[] argumentTypes; ! final InvocationPlugin value; /** ! * Used to lazily initialize {@link #resolved}. */ ! private final MetaAccessProvider metaAccess; ! private volatile ResolvedJavaMethod resolved; ! MethodKey(MetaAccessProvider metaAccess, InvocationPlugin data, boolean isStatic, boolean isOptional, String name, Type... argumentTypes) { ! this.metaAccess = metaAccess; ! this.value = data; this.isStatic = isStatic; - this.isOptional = isOptional; this.name = name; ! this.argumentTypes = argumentTypes; ! } ! ! @Override ! public boolean equals(Object obj) { ! if (obj instanceof MethodKey) { ! MethodKey that = (MethodKey) obj; ! boolean res = this.name.equals(that.name) && areEqual(this.argumentTypes, that.argumentTypes); ! assert !res || this.isStatic == that.isStatic; ! return res; ! } ! return false; ! } ! ! private static boolean areEqual(Type[] args1, Type[] args2) { ! if (args1.length == args2.length) { ! for (int i = 0; i < args1.length; i++) { ! if (!args1[i].getTypeName().equals(args2[i].getTypeName())) { ! return false; ! } ! } ! return true; ! } ! return false; ! } ! ! public int getDeclaredParameterCount() { ! return isStatic ? argumentTypes.length : argumentTypes.length - 1; ! } ! ! @Override ! public int hashCode() { ! return name.hashCode(); ! } ! ! private ResolvedJavaMethod resolve(Class<?> declaringClass) { ! if (resolved == null) { ! Executable method = resolveJava(declaringClass); ! if (method == null) { ! return null; ! } ! resolved = metaAccess.lookupJavaMethod(method); ! } ! return resolved; ! } ! ! private Executable resolveJava(Class<?> declaringClass) { ! try { ! Executable res; ! Class<?>[] parameterTypes = resolveTypes(argumentTypes, isStatic ? 0 : 1, argumentTypes.length); ! if (name.equals("<init>")) { ! res = declaringClass.getDeclaredConstructor(parameterTypes); ! } else { ! res = declaringClass.getDeclaredMethod(name, parameterTypes); ! } ! assert Modifier.isStatic(res.getModifiers()) == isStatic : res; ! return res; ! } catch (NoSuchMethodException | SecurityException e) { ! if (isOptional) { ! return null; ! } ! throw new InternalError(e); ! } } @Override public String toString() { ! StringBuilder sb = new StringBuilder(name).append('('); ! for (Type p : argumentTypes) { ! if (sb.charAt(sb.length() - 1) != '(') { ! sb.append(", "); ! } ! sb.append(p.getTypeName()); ! } ! return sb.append(')').toString(); } } ! private final MetaAccessProvider metaAccess; ! private final Map<String, ClassPlugins> registrations = new HashMap<>(); /** ! * Deferred registrations as well as guard for initialization. The guard uses double-checked ! * locking which is why this field is {@code volatile}. */ private volatile List<Runnable> deferredRegistrations = new ArrayList<>(); /** * Adds a {@link Runnable} for doing registration deferred until the first time --- 366,520 ---- * is non-static. Upon returning, element 0 will have been rewritten to * {@code declaringClass} */ @Override public void registerMethodSubstitution(Class<?> substituteDeclaringClass, String name, String substituteName, Type... argumentTypes) { + MethodSubstitutionPlugin plugin = createMethodSubstitution(substituteDeclaringClass, substituteName, argumentTypes); + plugins.register(plugin, false, allowOverwrite, declaringType, name, argumentTypes); + } + + public MethodSubstitutionPlugin createMethodSubstitution(Class<?> substituteDeclaringClass, String substituteName, Type... argumentTypes) { assert methodSubstitutionBytecodeProvider != null : "Registration used for method substitutions requires a non-null methodSubstitutionBytecodeProvider"; MethodSubstitutionPlugin plugin = new MethodSubstitutionPlugin(methodSubstitutionBytecodeProvider, substituteDeclaringClass, substituteName, argumentTypes); ! return plugin; } + } /** ! * Utility for registering plugins after Graal may have been initialized. Registrations made via ! * this class are not finalized until {@link #close} is called. */ ! public static class LateRegistration implements AutoCloseable { ! private InvocationPlugins plugins; ! private final List<Binding> bindings = new ArrayList<>(); ! private final Type declaringType; ! /** ! * Creates an object for registering {@link InvocationPlugin}s for methods declared by a ! * given class. ! * ! * @param plugins where to register the plugins ! * @param declaringType the class declaring the methods for which plugins will be registered ! * via this object ! */ ! public LateRegistration(InvocationPlugins plugins, Type declaringType) { ! this.plugins = plugins; ! this.declaringType = declaringType; } ! ! /** ! * Registers an invocation plugin for a given method. There must be no plugin currently ! * registered for {@code method}. ! * ! * @param argumentTypes the argument types of the method. Element 0 of this array must be ! * the {@link Class} value for {@link InvocationPlugin.Receiver} iff the method ! * is non-static. Upon returning, element 0 will have been rewritten to ! * {@code declaringClass} ! */ ! public void register(InvocationPlugin plugin, String name, Type... argumentTypes) { ! boolean isStatic = argumentTypes.length == 0 || argumentTypes[0] != InvocationPlugin.Receiver.class; ! if (!isStatic) { ! argumentTypes[0] = declaringType; } ! assert isStatic || argumentTypes[0] == declaringType; ! Binding binding = new Binding(plugin, isStatic, name, argumentTypes); ! bindings.add(binding); ! ! assert Checks.check(this.plugins, declaringType, binding); ! assert Checks.checkResolvable(false, declaringType, binding); } @Override ! public void close() { ! assert plugins != null : String.format("Late registrations of invocation plugins for %s is already closed", declaringType); ! plugins.registerLate(declaringType, bindings); ! plugins = null; } } /** ! * Associates an {@link InvocationPlugin} with the details of a method it substitutes. */ ! public static class Binding { ! /** ! * The plugin this binding is for. ! */ ! public final InvocationPlugin plugin; /** ! * Specifies if the associated method is static. */ ! public final boolean isStatic; ! /** ! * The name of the associated method. ! */ ! public final String name; /** ! * A partial ! * <a href="http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3.3">method ! * descriptor</a> for the associated method. The descriptor includes enclosing {@code '('} ! * and {@code ')'} characters but omits the return type suffix. */ ! public final String argumentsDescriptor; ! /** ! * Link in a list of bindings. ! */ ! private Binding next; ! Binding(InvocationPlugin data, boolean isStatic, String name, Type... argumentTypes) { ! this.plugin = data; this.isStatic = isStatic; this.name = name; ! StringBuilder buf = new StringBuilder(); ! buf.append('('); ! for (int i = isStatic ? 0 : 1; i < argumentTypes.length; i++) { ! buf.append(MetaUtil.toInternalName(argumentTypes[i].getTypeName())); ! } ! buf.append(')'); ! this.argumentsDescriptor = buf.toString(); ! assert !name.equals("<init>") || !isStatic : this; ! } ! ! Binding(ResolvedJavaMethod resolved, InvocationPlugin data) { ! this.plugin = data; ! this.isStatic = resolved.isStatic(); ! this.name = resolved.getName(); ! Signature sig = resolved.getSignature(); ! String desc = sig.toMethodDescriptor(); ! assert desc.indexOf(')') != -1 : desc; ! this.argumentsDescriptor = desc.substring(0, desc.indexOf(')') + 1); ! assert !name.equals("<init>") || !isStatic : this; } @Override public String toString() { ! return name + argumentsDescriptor; } } ! /** ! * Plugin registrations for already resolved methods. If non-null, then {@link #registrations} ! * is null and no further registrations can be made. ! */ ! private final Map<ResolvedJavaMethod, InvocationPlugin> resolvedRegistrations; ! /** ! * Map from class names in {@linkplain MetaUtil#toInternalName(String) internal} form to the ! * invocation plugin bindings for the class. Tf non-null, then {@link #resolvedRegistrations} ! * will be null. ! */ ! private final Map<String, ClassPlugins> registrations; /** ! * Deferred registrations as well as the guard for delimiting the initial registration phase. ! * The guard uses double-checked locking which is why this field is {@code volatile}. */ private volatile List<Runnable> deferredRegistrations = new ArrayList<>(); /** * Adds a {@link Runnable} for doing registration deferred until the first time
*** 510,632 **** assert deferredRegistrations != null : "registration is closed"; deferredRegistrations.add(deferrable); } /** ! * Per-class invocation plugins. */ ! protected static class ClassPlugins { ! private final Type declaringType; ! private final List<MethodKey> registrations = new ArrayList<>(); ! public ClassPlugins(Type declaringClass) { ! this.declaringType = declaringClass; ! } /** ! * Entry map that is initialized upon first call to {@link #get(ResolvedJavaMethod)}. * ! * Note: this must be volatile as threads may race to initialize it. */ ! private volatile Map<ResolvedJavaMethodKey, InvocationPlugin> entries; ! ! void initializeMap() { ! if (!isClosed()) { ! if (registrations.isEmpty()) { ! entries = Collections.emptyMap(); ! } else { ! Class<?> declaringClass = resolveType(declaringType, true); ! if (declaringClass == null) { ! // An optional type that could not be resolved ! entries = Collections.emptyMap(); ! } else { ! Map<ResolvedJavaMethodKey, InvocationPlugin> newEntries = new HashMap<>(); ! for (MethodKey methodKey : registrations) { ! ResolvedJavaMethod m = methodKey.resolve(declaringClass); ! if (m != null) { ! newEntries.put(new ResolvedJavaMethodKey(m), methodKey.value); ! if (entries != null) { ! // Another thread finished initializing entries first ! return; } } } ! entries = newEntries; } } } } ! public InvocationPlugin get(ResolvedJavaMethod method) { ! if (!isClosed()) { ! initializeMap(); } ! return entries.get(new ResolvedJavaMethodKey(method)); } ! ! public void register(MethodKey methodKey, boolean allowOverwrite) { ! assert !isClosed() : "registration is closed: " + methodKey + " " + Arrays.toString(entries.keySet().toArray()); ! if (allowOverwrite) { ! int index = registrations.indexOf(methodKey); ! if (index >= 0) { ! registrations.set(index, methodKey); ! return; } ! } else { ! assert !registrations.contains(methodKey) : "a value is already registered for " + declaringType + "." + methodKey; } - registrations.add(methodKey); } ! public boolean isClosed() { ! return entries != null; } } /** ! * Adds an entry to this map for a specified method. * ! * @param value value to be associated with the specified method * @param isStatic specifies if the method is static - * @param isOptional specifies if the method is optional * @param declaringClass the class declaring the method * @param name the name of the method * @param argumentTypes the argument types of the method. Element 0 of this array must be * {@code declaringClass} iff the method is non-static. * @return an object representing the method */ ! MethodKey put(InvocationPlugin value, boolean isStatic, boolean isOptional, boolean allowOverwrite, Type declaringClass, String name, Type... argumentTypes) { assert isStatic || argumentTypes[0] == declaringClass; - String internalName = MetaUtil.toInternalName(declaringClass.getTypeName()); ClassPlugins classPlugins = registrations.get(internalName); if (classPlugins == null) { ! classPlugins = new ClassPlugins(declaringClass); registrations.put(internalName, classPlugins); } ! assert isStatic || argumentTypes[0] == declaringClass; ! MethodKey methodKey = new MethodKey(metaAccess, value, isStatic, isOptional, name, argumentTypes); ! classPlugins.register(methodKey, allowOverwrite); ! return methodKey; ! } ! ! /** ! * Determines if a method denoted by a given {@link MethodKey} is in this map. ! */ ! boolean containsKey(Type declaringType, MethodKey key) { ! String internalName = MetaUtil.toInternalName(declaringType.getTypeName()); ! ClassPlugins classPlugins = registrations.get(internalName); ! return classPlugins != null && classPlugins.registrations.contains(key); } InvocationPlugin get(ResolvedJavaMethod method) { flushDeferrables(); ! String internalName = method.getDeclaringClass().getName(); ClassPlugins classPlugins = registrations.get(internalName); if (classPlugins != null) { ! return classPlugins.get(method); } return null; } private void flushDeferrables() { --- 524,686 ---- assert deferredRegistrations != null : "registration is closed"; deferredRegistrations.add(deferrable); } /** ! * Support for registering plugins once this object may be accessed by multiple threads. */ ! private volatile LateClassPlugins lateRegistrations; ! /** ! * Per-class bindings. ! */ ! static class ClassPlugins { ! /** ! * Maps method names to binding lists. ! */ ! private final Map<String, Binding> bindings = new HashMap<>(); /** ! * Gets the invocation plugin for a given method. * ! * @return the invocation plugin for {@code method} or {@code null} */ ! InvocationPlugin get(ResolvedJavaMethod method) { ! assert !method.isBridge(); ! Binding binding = bindings.get(method.getName()); ! while (binding != null) { ! if (method.isStatic() == binding.isStatic) { ! if (method.getSignature().toMethodDescriptor().startsWith(binding.argumentsDescriptor)) { ! return binding.plugin; } } + binding = binding.next; } ! return null; } + + public void register(Binding binding, boolean allowOverwrite) { + if (allowOverwrite) { + if (lookup(binding) != null) { + register(binding); + return; } + } else { + assert lookup(binding) == null : "a value is already registered for " + binding; } + register(binding); } ! InvocationPlugin lookup(Binding binding) { ! Binding b = bindings.get(binding.name); ! while (b != null) { ! if (b.isStatic == binding.isStatic && b.argumentsDescriptor.equals(binding.argumentsDescriptor)) { ! return b.plugin; } ! b = b.next; } ! return null; } ! ! /** ! * Registers {@code binding}. ! */ ! void register(Binding binding) { ! Binding head = bindings.get(binding.name); ! assert binding.next == null; ! binding.next = head; ! bindings.put(binding.name, binding); } } ! static class LateClassPlugins extends ClassPlugins { ! static final String CLOSED_LATE_CLASS_PLUGIN = "-----"; ! private final String className; ! private final LateClassPlugins next; ! ! LateClassPlugins(LateClassPlugins next, String className) { ! assert next == null || next.className != CLOSED_LATE_CLASS_PLUGIN : "Late registration of invocation plugins is closed"; ! this.next = next; ! this.className = className; } } /** ! * Registers a binding of a method to an invocation plugin. * ! * @param plugin invocation plugin to be associated with the specified method * @param isStatic specifies if the method is static * @param declaringClass the class declaring the method * @param name the name of the method * @param argumentTypes the argument types of the method. Element 0 of this array must be * {@code declaringClass} iff the method is non-static. * @return an object representing the method */ ! Binding put(InvocationPlugin plugin, boolean isStatic, boolean allowOverwrite, Type declaringClass, String name, Type... argumentTypes) { ! assert resolvedRegistrations == null : "registration is closed"; ! String internalName = MetaUtil.toInternalName(declaringClass.getTypeName()); assert isStatic || argumentTypes[0] == declaringClass; + assert deferredRegistrations != null : "initial registration is closed - use " + LateRegistration.class.getName() + " for late registrations"; ClassPlugins classPlugins = registrations.get(internalName); if (classPlugins == null) { ! classPlugins = new ClassPlugins(); registrations.put(internalName, classPlugins); } ! Binding binding = new Binding(plugin, isStatic, name, argumentTypes); ! classPlugins.register(binding, allowOverwrite); ! return binding; } InvocationPlugin get(ResolvedJavaMethod method) { + if (resolvedRegistrations != null) { + return resolvedRegistrations.get(method); + } else { + if (!method.isBridge()) { + ResolvedJavaType declaringClass = method.getDeclaringClass(); flushDeferrables(); ! String internalName = declaringClass.getName(); ClassPlugins classPlugins = registrations.get(internalName); + InvocationPlugin res = null; if (classPlugins != null) { ! res = classPlugins.get(method); ! } ! if (res == null) { ! LateClassPlugins lcp = findLateClassPlugins(internalName); ! if (lcp != null) { ! res = lcp.get(method); ! } ! } ! if (res != null) { ! if (canBeIntrinsified(declaringClass)) { ! return res; ! } ! } ! } else { ! // Supporting plugins for bridge methods would require including ! // the return type in the registered signature. Until needed, ! // this extra complexity is best avoided. ! } ! } ! return null; ! } ! ! /** ! * Determines if methods in a given class can have invocation plugins. ! * ! * @param declaringClass the class to test ! */ ! protected boolean canBeIntrinsified(ResolvedJavaType declaringClass) { ! return true; ! } ! ! LateClassPlugins findLateClassPlugins(String internalClassName) { ! for (LateClassPlugins lcp = lateRegistrations; lcp != null; lcp = lcp.next) { ! if (lcp.className.equals(internalClassName)) { ! return lcp; ! } } return null; } private void flushDeferrables() {
*** 637,723 **** deferrable.run(); } deferredRegistrations = null; } } - for (Map.Entry<String, ClassPlugins> e : registrations.entrySet()) { - e.getValue().initializeMap(); } } } /** ! * Disallows new registrations of new plugins, and creates the internal tables for method ! * lookup. */ public void closeRegistration() { flushDeferrables(); - for (Map.Entry<String, ClassPlugins> e : registrations.entrySet()) { - e.getValue().initializeMap(); - } } ! public int size() { ! return registrations.size(); } /** * The plugins {@linkplain #lookupInvocation(ResolvedJavaMethod) searched} before searching in * this object. */ protected final InvocationPlugins parent; ! private InvocationPlugins(InvocationPlugins parent, MetaAccessProvider metaAccess) { ! this.metaAccess = metaAccess; ! InvocationPlugins p = parent; ! this.parent = p; } /** ! * Creates a set of invocation plugins with a non-null {@linkplain #getParent() parent}. */ public InvocationPlugins(InvocationPlugins parent) { ! this(parent, parent.getMetaAccess()); } ! public InvocationPlugins(Map<ResolvedJavaMethod, InvocationPlugin> plugins, InvocationPlugins parent, MetaAccessProvider metaAccess) { ! this.metaAccess = metaAccess; this.parent = parent; ! this.deferredRegistrations = null; for (Map.Entry<ResolvedJavaMethod, InvocationPlugin> entry : plugins.entrySet()) { ! ResolvedJavaMethod method = entry.getKey(); ! InvocationPlugin plugin = entry.getValue(); ! ! String internalName = method.getDeclaringClass().getName(); ! ClassPlugins classPlugins = registrations.get(internalName); ! if (classPlugins == null) { ! classPlugins = new ClassPlugins(null); ! registrations.put(internalName, classPlugins); ! classPlugins.entries = new HashMap<>(); ! } ! ! classPlugins.entries.put(new ResolvedJavaMethodKey(method), plugin); ! } } ! ! public MetaAccessProvider getMetaAccess() { ! return metaAccess; ! } ! ! public InvocationPlugins(MetaAccessProvider metaAccess) { ! this(null, metaAccess); } protected void register(InvocationPlugin plugin, boolean isOptional, boolean allowOverwrite, Type declaringClass, String name, Type... argumentTypes) { boolean isStatic = argumentTypes.length == 0 || argumentTypes[0] != InvocationPlugin.Receiver.class; if (!isStatic) { argumentTypes[0] = declaringClass; } ! MethodKey methodKey = put(plugin, isStatic, isOptional, allowOverwrite, declaringClass, name, argumentTypes); ! assert Checker.check(this, declaringClass, methodKey, plugin); } /** * Registers an invocation plugin for a given method. There must be no plugin currently * registered for {@code method}. --- 691,784 ---- deferrable.run(); } deferredRegistrations = null; } } } } + + synchronized void registerLate(Type declaringType, List<Binding> bindings) { + String internalName = MetaUtil.toInternalName(declaringType.getTypeName()); + assert findLateClassPlugins(internalName) == null : "Cannot have more than one late registration of invocation plugins for " + internalName; + LateClassPlugins lateClassPlugins = new LateClassPlugins(lateRegistrations, internalName); + for (Binding b : bindings) { + lateClassPlugins.register(b); + } + lateRegistrations = lateClassPlugins; + } + + private synchronized boolean closeLateRegistrations() { + if (lateRegistrations == null || lateRegistrations.className != CLOSED_LATE_CLASS_PLUGIN) { + lateRegistrations = new LateClassPlugins(lateRegistrations, CLOSED_LATE_CLASS_PLUGIN); + } + return true; } /** ! * Processes deferred registrations and then closes this object for future registration. */ public void closeRegistration() { + assert closeLateRegistrations(); flushDeferrables(); } ! public boolean isEmpty() { ! if (resolvedRegistrations != null) { ! return resolvedRegistrations.isEmpty(); ! } ! return registrations.size() == 0 && lateRegistrations == null; } /** * The plugins {@linkplain #lookupInvocation(ResolvedJavaMethod) searched} before searching in * this object. */ protected final InvocationPlugins parent; ! /** ! * Creates a set of invocation plugins with no parent. ! */ ! public InvocationPlugins() { ! this(null); } /** ! * Creates a set of invocation plugins. ! * ! * @param parent if non-null, this object will be searched first when looking up plugins */ public InvocationPlugins(InvocationPlugins parent) { ! InvocationPlugins p = parent; ! this.parent = p; ! this.registrations = new HashMap<>(); ! this.resolvedRegistrations = null; } ! /** ! * Creates a closed set of invocation plugins for a set of resolved methods. Such an object ! * cannot have further plugins registered. ! */ ! public InvocationPlugins(Map<ResolvedJavaMethod, InvocationPlugin> plugins, InvocationPlugins parent) { this.parent = parent; ! this.registrations = null; this.deferredRegistrations = null; + Map<ResolvedJavaMethod, InvocationPlugin> map = new HashMap<>(plugins.size()); for (Map.Entry<ResolvedJavaMethod, InvocationPlugin> entry : plugins.entrySet()) { ! map.put(entry.getKey(), entry.getValue()); } ! this.resolvedRegistrations = map; } protected void register(InvocationPlugin plugin, boolean isOptional, boolean allowOverwrite, Type declaringClass, String name, Type... argumentTypes) { boolean isStatic = argumentTypes.length == 0 || argumentTypes[0] != InvocationPlugin.Receiver.class; if (!isStatic) { argumentTypes[0] = declaringClass; } ! Binding binding = put(plugin, isStatic, allowOverwrite, declaringClass, name, argumentTypes); ! assert Checks.check(this, declaringClass, binding); ! assert Checks.checkResolvable(isOptional, declaringClass, binding); } /** * Registers an invocation plugin for a given method. There must be no plugin currently * registered for {@code method}.
*** 763,810 **** } return get(method); } /** ! * Gets the set of methods for which invocation plugins have been registered. Once this method ! * is called, no further registrations can be made. */ ! public Set<ResolvedJavaMethod> getMethods() { ! Set<ResolvedJavaMethod> res = new HashSet<>(); ! if (parent != null) { ! res.addAll(parent.getMethods()); } flushDeferrables(); ! for (ClassPlugins cp : registrations.values()) { ! for (ResolvedJavaMethodKey key : cp.entries.keySet()) { ! res.add(key.method); } } return res; } /** * Gets the invocation plugins {@linkplain #lookupInvocation(ResolvedJavaMethod) searched} * before searching in this object. */ public InvocationPlugins getParent() { return parent; } @Override public String toString() { StringBuilder buf = new StringBuilder(); ! registrations.forEach((name, cp) -> buf.append(name).append('.').append(cp).append(", ")); ! String s = buf.toString(); if (buf.length() != 0) { ! s = s.substring(buf.length() - ", ".length()); } ! return s + " / parent: " + this.parent; } ! private static class Checker { ! private static final int MAX_ARITY = 5; /** * The set of all {@link InvocationPlugin#apply} method signatures. */ static final Class<?>[][] SIGS; --- 824,923 ---- } return get(method); } /** ! * Gets the set of registered invocation plugins. ! * ! * @return a map from class names in {@linkplain MetaUtil#toInternalName(String) internal} form ! * to the invocation plugin bindings for methods in the class */ ! public Map<String, List<Binding>> getBindings(boolean includeParents) { ! Map<String, List<Binding>> res = new HashMap<>(); ! if (parent != null && includeParents) { ! res.putAll(parent.getBindings(true)); ! } ! if (resolvedRegistrations != null) { ! for (Map.Entry<ResolvedJavaMethod, InvocationPlugin> e : resolvedRegistrations.entrySet()) { ! ResolvedJavaMethod method = e.getKey(); ! InvocationPlugin plugin = e.getValue(); ! String type = method.getDeclaringClass().getName(); ! List<Binding> bindings = res.get(type); ! if (bindings == null) { ! bindings = new ArrayList<>(); ! res.put(type, bindings); } + bindings.add(new Binding(method, plugin)); + } + } else { flushDeferrables(); ! for (Map.Entry<String, ClassPlugins> e : registrations.entrySet()) { ! String type = e.getKey(); ! ClassPlugins cp = e.getValue(); ! collectBindingsTo(res, type, cp); ! } ! for (LateClassPlugins lcp = lateRegistrations; lcp != null; lcp = lcp.next) { ! String type = lcp.className; ! collectBindingsTo(res, type, lcp); } } return res; } + private static void collectBindingsTo(Map<String, List<Binding>> res, String type, ClassPlugins cp) { + for (Map.Entry<String, Binding> e : cp.bindings.entrySet()) { + List<Binding> bindings = res.get(type); + if (bindings == null) { + bindings = new ArrayList<>(); + res.put(type, bindings); + } + for (Binding b = e.getValue(); b != null; b = b.next) { + bindings.add(b); + } + } + } + /** * Gets the invocation plugins {@linkplain #lookupInvocation(ResolvedJavaMethod) searched} * before searching in this object. */ public InvocationPlugins getParent() { return parent; } @Override public String toString() { + List<String> all = new ArrayList<>(); + for (Map.Entry<String, List<Binding>> e : getBindings(false).entrySet()) { + String c = MetaUtil.internalNameToJava(e.getKey(), true, false); + for (Binding b : e.getValue()) { + all.add(c + '.' + b); + } + } + Collections.sort(all); StringBuilder buf = new StringBuilder(); ! String nl = String.format("%n"); ! for (String s : all) { ! if (buf.length() != 0) { ! buf.append(nl); ! } ! buf.append(s); ! } ! if (parent != null) { if (buf.length() != 0) { ! buf.append(nl); } ! buf.append("// parent").append(nl).append(parent); ! } ! return buf.toString(); } ! /** ! * Code only used in assertions. Putting this in a separate class reduces class load time. ! */ ! private static class Checks { ! private static final int MAX_ARITY = 7; /** * The set of all {@link InvocationPlugin#apply} method signatures. */ static final Class<?>[][] SIGS;
*** 826,839 **** 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), ValueNode.class.getSimpleName()); SIGS = sigs.toArray(new Class<?>[sigs.size()][]); } ! public static boolean check(InvocationPlugins plugins, Type declaringType, MethodKey method, InvocationPlugin plugin) { InvocationPlugins p = plugins.parent; while (p != null) { ! assert !p.containsKey(declaringType, method) : "a plugin is already registered for " + method; p = p.parent; } if (plugin instanceof ForeignCallPlugin || plugin instanceof GeneratedInvocationPlugin) { return true; } --- 939,959 ---- 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), ValueNode.class.getSimpleName()); SIGS = sigs.toArray(new Class<?>[sigs.size()][]); } ! static boolean containsBinding(InvocationPlugins p, Type declaringType, Binding key) { ! String internalName = MetaUtil.toInternalName(declaringType.getTypeName()); ! ClassPlugins classPlugins = p.registrations.get(internalName); ! return classPlugins != null && classPlugins.lookup(key) != null; ! } ! ! public static boolean check(InvocationPlugins plugins, Type declaringType, Binding binding) { ! InvocationPlugin plugin = binding.plugin; InvocationPlugins p = plugins.parent; while (p != null) { ! assert !containsBinding(p, declaringType, binding) : "a plugin is already registered for " + binding; p = p.parent; } if (plugin instanceof ForeignCallPlugin || plugin instanceof GeneratedInvocationPlugin) { return true; }
*** 841,861 **** MethodSubstitutionPlugin msplugin = (MethodSubstitutionPlugin) plugin; Method substitute = msplugin.getJavaSubstitute(); assert substitute.getAnnotation(MethodSubstitution.class) != null : format("Substitute method must be annotated with @%s: %s", MethodSubstitution.class.getSimpleName(), substitute); return true; } ! int arguments = method.getDeclaredParameterCount(); ! assert arguments < SIGS.length : format("need to extend %s to support method with %d arguments: %s", InvocationPlugin.class.getSimpleName(), arguments, method); for (Method m : plugin.getClass().getDeclaredMethods()) { if (m.getName().equals("apply")) { Class<?>[] parameterTypes = m.getParameterTypes(); if (Arrays.equals(SIGS[arguments], parameterTypes)) { return true; } } } ! throw new AssertionError(format("graph builder plugin for %s not found", method)); } } /** * Checks a set of nodes added to the graph by an {@link InvocationPlugin}. --- 961,998 ---- MethodSubstitutionPlugin msplugin = (MethodSubstitutionPlugin) plugin; Method substitute = msplugin.getJavaSubstitute(); assert substitute.getAnnotation(MethodSubstitution.class) != null : format("Substitute method must be annotated with @%s: %s", MethodSubstitution.class.getSimpleName(), substitute); return true; } ! int arguments = parseParameters(binding.argumentsDescriptor).size(); ! assert arguments < SIGS.length : format("need to extend %s to support method with %d arguments: %s", InvocationPlugin.class.getSimpleName(), arguments, binding); for (Method m : plugin.getClass().getDeclaredMethods()) { if (m.getName().equals("apply")) { Class<?>[] parameterTypes = m.getParameterTypes(); if (Arrays.equals(SIGS[arguments], parameterTypes)) { return true; } } } ! throw new AssertionError(format("graph builder plugin for %s not found", binding)); ! } ! ! static boolean checkResolvable(boolean isOptional, Type declaringType, Binding binding) { ! Class<?> declaringClass = InvocationPlugins.resolveType(declaringType, isOptional); ! if (declaringClass == null) { ! return true; ! } ! if (binding.name.equals("<init>")) { ! if (resolveConstructor(declaringClass, binding) == null && !isOptional) { ! throw new AssertionError(String.format("Constructor not found: %s%s", declaringClass.getName(), binding.argumentsDescriptor)); ! } ! } else { ! if (resolveMethod(declaringClass, binding) == null && !isOptional) { ! throw new AssertionError(String.format("Method not found: %s.%s%s", declaringClass.getName(), binding.name, binding.argumentsDescriptor)); ! } ! } ! return true; } } /** * Checks a set of nodes added to the graph by an {@link InvocationPlugin}.
*** 902,934 **** */ public static Class<?> resolveType(Type type, boolean optional) { if (type instanceof Class) { return (Class<?>) type; } ! if (optional && type instanceof OptionalLazySymbol) { return ((OptionalLazySymbol) type).resolve(); } return resolveClass(type.getTypeName(), optional); } ! private static final Class<?>[] NO_CLASSES = {}; /** ! * Resolves an array of {@link Type}s to an array of {@link Class}es. * ! * @param types the types to resolve ! * @param from the initial index of the range to be resolved, inclusive ! * @param to the final index of the range to be resolved, exclusive ! * @return the resolved class or null if resolution fails and {@code optional} is true */ ! public static Class<?>[] resolveTypes(Type[] types, int from, int to) { ! int length = to - from; ! if (length <= 0) { ! return NO_CLASSES; ! } ! Class<?>[] classes = new Class<?>[length]; ! for (int i = 0; i < length; i++) { ! classes[i] = resolveType(types[i + from], false); } ! return classes; } } --- 1039,1159 ---- */ public static Class<?> resolveType(Type type, boolean optional) { if (type instanceof Class) { return (Class<?>) type; } ! if (type instanceof OptionalLazySymbol) { return ((OptionalLazySymbol) type).resolve(); } return resolveClass(type.getTypeName(), optional); } ! private static List<String> toInternalTypeNames(Class<?>[] types) { ! String[] res = new String[types.length]; ! for (int i = 0; i < types.length; i++) { ! res[i] = MetaUtil.toInternalName(types[i].getTypeName()); ! } ! return Arrays.asList(res); ! } /** ! * Resolves a given binding to a method in a given class. If more than one method with the ! * parameter types matching {@code binding} is found and the return types of all the matching ! * methods form an inheritance chain, the one with the most specific type is returned; otherwise ! * {@link NoSuchMethodError} is thrown. * ! * @param declaringClass the class to search for a method matching {@code binding} ! * @return the method (if any) in {@code declaringClass} matching binding ! */ ! public static Method resolveMethod(Class<?> declaringClass, Binding binding) { ! if (binding.name.equals("<init>")) { ! return null; ! } ! Method[] methods = declaringClass.getDeclaredMethods(); ! List<String> parameterTypeNames = parseParameters(binding.argumentsDescriptor); ! for (int i = 0; i < methods.length; ++i) { ! Method m = methods[i]; ! if (binding.isStatic == Modifier.isStatic(m.getModifiers()) && m.getName().equals(binding.name)) { ! if (parameterTypeNames.equals(toInternalTypeNames(m.getParameterTypes()))) { ! for (int j = i + 1; j < methods.length; ++j) { ! Method other = methods[j]; ! if (binding.isStatic == Modifier.isStatic(other.getModifiers()) && other.getName().equals(binding.name)) { ! if (parameterTypeNames.equals(toInternalTypeNames(other.getParameterTypes()))) { ! if (m.getReturnType().isAssignableFrom(other.getReturnType())) { ! // `other` has a more specific return type - choose it ! // (m is most likely a bridge method) ! m = other; ! } else { ! if (!other.getReturnType().isAssignableFrom(m.getReturnType())) { ! throw new NoSuchMethodError(String.format( ! "Found 2 methods with same name and parameter types but unrelated return types:%n %s%n %s", m, other)); ! } ! } ! } ! } ! } ! return m; ! } ! } ! } ! return null; ! } ! ! /** ! * Resolves a given binding to a constructor in a given class. ! * ! * @param declaringClass the class to search for a constructor matching {@code binding} ! * @return the constructor (if any) in {@code declaringClass} matching binding */ ! public static Constructor<?> resolveConstructor(Class<?> declaringClass, Binding binding) { ! if (!binding.name.equals("<init>")) { ! return null; ! } ! Constructor<?>[] constructors = declaringClass.getDeclaredConstructors(); ! List<String> parameterTypeNames = parseParameters(binding.argumentsDescriptor); ! for (int i = 0; i < constructors.length; ++i) { ! Constructor<?> c = constructors[i]; ! if (parameterTypeNames.equals(toInternalTypeNames(c.getParameterTypes()))) { ! return c; } ! } ! return null; ! } ! ! private static List<String> parseParameters(String argumentsDescriptor) { ! assert argumentsDescriptor.startsWith("(") && argumentsDescriptor.endsWith(")") : argumentsDescriptor; ! List<String> res = new ArrayList<>(); ! int cur = 1; ! int end = argumentsDescriptor.length() - 1; ! while (cur != end) { ! char first; ! int start = cur; ! do { ! first = argumentsDescriptor.charAt(cur++); ! } while (first == '['); ! ! switch (first) { ! case 'L': ! int endObject = argumentsDescriptor.indexOf(';', cur); ! if (endObject == -1) { ! throw new GraalError("Invalid object type at index %d in signature: %s", cur, argumentsDescriptor); ! } ! cur = endObject + 1; ! break; ! case 'V': ! case 'I': ! case 'B': ! case 'C': ! case 'D': ! case 'F': ! case 'J': ! case 'S': ! case 'Z': ! break; ! default: ! throw new GraalError("Invalid character at index %d in signature: %s", cur, argumentsDescriptor); ! } ! res.add(argumentsDescriptor.substring(start, cur)); ! } ! return res; } }
< prev index next >