< 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 >