8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24
25 package org.graalvm.compiler.nodes.graphbuilderconf;
26
27 import static java.lang.String.format;
28 import static org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.LateClassPlugins.CLOSED_LATE_CLASS_PLUGIN;
29
30 import java.lang.reflect.Constructor;
31 import java.lang.reflect.Method;
32 import java.lang.reflect.Modifier;
33 import java.lang.reflect.Type;
34 import java.util.ArrayList;
35 import java.util.Arrays;
36 import java.util.Collections;
37 import java.util.List;
38 import java.util.Map;
39
40 import jdk.internal.vm.compiler.collections.EconomicMap;
41 import jdk.internal.vm.compiler.collections.Equivalence;
42 import jdk.internal.vm.compiler.collections.MapCursor;
43 import jdk.internal.vm.compiler.collections.Pair;
44 import jdk.internal.vm.compiler.collections.UnmodifiableEconomicMap;
45 import jdk.internal.vm.compiler.collections.UnmodifiableMapCursor;
46 import org.graalvm.compiler.api.replacements.MethodSubstitution;
47 import org.graalvm.compiler.api.replacements.MethodSubstitutionRegistry;
127 public ResolvedJavaType getResolved() {
128 return resolved;
129 }
130
131 @Override
132 public String toString() {
133 return resolved.toJavaName();
134 }
135 }
136
137 /**
138 * A symbol that is lazily {@linkplain OptionalLazySymbol#resolve() resolved} to a {@link Type}.
139 */
140 static class OptionalLazySymbol implements Type {
141 private static final Class<?> MASK_NULL = OptionalLazySymbol.class;
142 private final String name;
143 private Class<?> resolved;
144
145 OptionalLazySymbol(String name) {
146 this.name = name;
147 }
148
149 @Override
150 public String getTypeName() {
151 return name;
152 }
153
154 /**
155 * Gets the resolved {@link Class} corresponding to this symbol or {@code null} if
156 * resolution fails.
157 */
158 public Class<?> resolve() {
159 if (resolved == null) {
160 Class<?> resolvedOrNull = resolveClass(name, true);
161 resolved = resolvedOrNull == null ? MASK_NULL : resolvedOrNull;
162 }
163 return resolved == MASK_NULL ? null : resolved;
164 }
165
166 @Override
167 public String toString() {
168 return name;
169 }
170 }
171
172 /**
173 * Utility for {@linkplain InvocationPlugins#register(InvocationPlugin, Class, String, Class...)
174 * registration} of invocation plugins.
175 */
176 public static class Registration implements MethodSubstitutionRegistry {
177
178 private final InvocationPlugins plugins;
179 private final Type declaringType;
205 *
206 * @param plugins where to register the plugins
207 * @param declaringType the class declaring the methods for which plugins will be registered
208 * via this object
209 * @param methodSubstitutionBytecodeProvider provider used to get the bytecodes to parse for
210 * method substitutions
211 */
212 public Registration(InvocationPlugins plugins, Type declaringType, BytecodeProvider methodSubstitutionBytecodeProvider) {
213 this.plugins = plugins;
214 this.declaringType = declaringType;
215 this.methodSubstitutionBytecodeProvider = methodSubstitutionBytecodeProvider;
216 }
217
218 /**
219 * Creates an object for registering {@link InvocationPlugin}s for methods declared by a
220 * given class.
221 *
222 * @param plugins where to register the plugins
223 * @param declaringClassName the name of the class class declaring the methods for which
224 * plugins will be registered via this object
225 * @param methodSubstitutionBytecodeProvider provider used to get the bytecodes to parse for
226 * method substitutions
227 */
228 public Registration(InvocationPlugins plugins, String declaringClassName, BytecodeProvider methodSubstitutionBytecodeProvider) {
229 this.plugins = plugins;
230 this.declaringType = new OptionalLazySymbol(declaringClassName);
231 this.methodSubstitutionBytecodeProvider = methodSubstitutionBytecodeProvider;
232 }
233
234 /**
235 * Configures this registration to allow or disallow overwriting of invocation plugins.
236 */
237 public Registration setAllowOverwrite(boolean allowOverwrite) {
238 this.allowOverwrite = allowOverwrite;
239 return this;
240 }
241
242 /**
243 * Registers a plugin for a method with no arguments.
244 *
435 /**
436 * Registers an invocation plugin for a given method. There must be no plugin currently
437 * registered for {@code method}.
438 *
439 * @param argumentTypes the argument types of the method. Element 0 of this array must be
440 * the {@link Class} value for {@link InvocationPlugin.Receiver} iff the method
441 * is non-static. Upon returning, element 0 will have been rewritten to
442 * {@code declaringClass}
443 */
444 public void register(InvocationPlugin plugin, String name, Type... argumentTypes) {
445 assert plugins != null : String.format("Late registrations of invocation plugins for %s is already closed", declaringType);
446 boolean isStatic = argumentTypes.length == 0 || argumentTypes[0] != InvocationPlugin.Receiver.class;
447 if (!isStatic) {
448 argumentTypes[0] = declaringType;
449 }
450
451 assert isStatic || argumentTypes[0] == declaringType;
452 Binding binding = new Binding(plugin, isStatic, name, argumentTypes);
453 bindings.add(binding);
454
455 assert Checks.check(this.plugins, declaringType, binding);
456 assert Checks.checkResolvable(false, declaringType, binding);
457 }
458
459 @Override
460 public void close() {
461 assert plugins != null : String.format("Late registrations of invocation plugins for %s is already closed", declaringType);
462 plugins.registerLate(declaringType, bindings);
463 plugins = null;
464 }
465 }
466
467 /**
468 * Associates an {@link InvocationPlugin} with the details of a method it substitutes.
469 */
470 public static class Binding {
471 /**
472 * The plugin this binding is for.
473 */
474 public final InvocationPlugin plugin;
475
476 /**
719
720 /**
721 * Determines if methods in a given class can have invocation plugins.
722 *
723 * @param declaringClass the class to test
724 */
725 public boolean canBeIntrinsified(ResolvedJavaType declaringClass) {
726 return true;
727 }
728
729 LateClassPlugins findLateClassPlugins(String internalClassName) {
730 for (LateClassPlugins lcp = lateRegistrations; lcp != null; lcp = lcp.next) {
731 if (lcp.className.equals(internalClassName)) {
732 return lcp;
733 }
734 }
735 return null;
736 }
737
738 @SuppressWarnings("serial")
739 static class InvocationPlugRegistrationError extends GraalError {
740 InvocationPlugRegistrationError(Throwable cause) {
741 super(cause);
742 }
743 }
744
745 private void flushDeferrables() {
746 if (deferredRegistrations != null) {
747 synchronized (this) {
748 if (deferredRegistrations != null) {
749 try {
750 for (Runnable deferrable : deferredRegistrations) {
751 deferrable.run();
752 }
753 deferredRegistrations = null;
754 } catch (InvocationPlugRegistrationError t) {
755 throw t;
756 } catch (Throwable t) {
757 /*
758 * Something went wrong during registration but it's possible we'll end up
759 * coming back into this code. nulling out deferredRegistrations would just
760 * cause other things to break and rerunning them would cause errors about
761 * already registered plugins, so rethrow the original exception during
762 * later invocations.
763 */
764 deferredRegistrations.clear();
765 Runnable rethrow = new Runnable() {
766 @Override
767 public void run() {
768 throw new InvocationPlugRegistrationError(t);
769 }
770 };
771 deferredRegistrations.add(rethrow);
772 rethrow.run();
773 }
774 }
775 }
776 }
777 }
778
779 private volatile EconomicMap<String, List<Binding>> testExtensions;
780
781 private static int findBinding(List<Binding> list, Binding key) {
782 for (int i = 0; i < list.size(); i++) {
783 Binding b = list.get(i);
784 if (b.isStatic == key.isStatic && b.name.equals(key.name) && b.argumentsDescriptor.equals(key.argumentsDescriptor)) {
785 return i;
786 }
787 }
788 return -1;
943 * cannot have further plugins registered.
944 */
945 public InvocationPlugins(Map<ResolvedJavaMethod, InvocationPlugin> plugins, InvocationPlugins parent) {
946 this.parent = parent;
947 this.registrations = null;
948 this.deferredRegistrations = null;
949 EconomicMap<ResolvedJavaMethod, InvocationPlugin> map = EconomicMap.create(plugins.size());
950
951 for (Map.Entry<ResolvedJavaMethod, InvocationPlugin> entry : plugins.entrySet()) {
952 map.put(entry.getKey(), entry.getValue());
953 }
954 this.resolvedRegistrations = map;
955 }
956
957 protected void register(InvocationPlugin plugin, boolean isOptional, boolean allowOverwrite, Type declaringClass, String name, Type... argumentTypes) {
958 boolean isStatic = argumentTypes.length == 0 || argumentTypes[0] != InvocationPlugin.Receiver.class;
959 if (!isStatic) {
960 argumentTypes[0] = declaringClass;
961 }
962 Binding binding = put(plugin, isStatic, allowOverwrite, declaringClass, name, argumentTypes);
963 assert Checks.check(this, declaringClass, binding);
964 assert Checks.checkResolvable(isOptional, declaringClass, binding);
965 }
966
967 /**
968 * Registers an invocation plugin for a given method. There must be no plugin currently
969 * registered for {@code method}.
970 *
971 * @param argumentTypes the argument types of the method. Element 0 of this array must be the
972 * {@link Class} value for {@link InvocationPlugin.Receiver} iff the method is
973 * non-static. Upon returning, element 0 will have been rewritten to
974 * {@code declaringClass}
975 */
976 public final void register(InvocationPlugin plugin, Type declaringClass, String name, Type... argumentTypes) {
977 register(plugin, false, false, declaringClass, name, argumentTypes);
978 }
979
980 public final void register(InvocationPlugin plugin, String declaringClass, String name, Type... argumentTypes) {
981 register(plugin, false, false, new OptionalLazySymbol(declaringClass), name, argumentTypes);
982 }
983
984 /**
987 *
988 * @param argumentTypes the argument types of the method. Element 0 of this array must be the
989 * {@link Class} value for {@link InvocationPlugin.Receiver} iff the method is
990 * non-static. Upon returning, element 0 will have been rewritten to
991 * {@code declaringClass}
992 */
993 public final void registerOptional(InvocationPlugin plugin, Type declaringClass, String name, Type... argumentTypes) {
994 register(plugin, true, false, declaringClass, name, argumentTypes);
995 }
996
997 /**
998 * Gets the plugin for a given method.
999 *
1000 * @param method the method to lookup
1001 * @return the plugin associated with {@code method} or {@code null} if none exists
1002 */
1003 public InvocationPlugin lookupInvocation(ResolvedJavaMethod method) {
1004 if (parent != null) {
1005 InvocationPlugin plugin = parent.lookupInvocation(method);
1006 if (plugin != null) {
1007 return plugin;
1008 }
1009 }
1010 return get(method);
1011 }
1012
1013 /**
1014 * Gets the set of registered invocation plugins.
1015 *
1016 * @return a map from class names in {@linkplain MetaUtil#toInternalName(String) internal} form
1017 * to the invocation plugin bindings for methods in the class
1018 */
1019 public EconomicMap<String, List<Binding>> getBindings(boolean includeParents) {
1020 return getBindings(includeParents, true);
1021 }
1022
1023 /**
1024 * Gets the set of registered invocation plugins.
1025 *
1026 * @return a map from class names in {@linkplain MetaUtil#toInternalName(String) internal} form
1027 * to the invocation plugin bindings for methods in the class
1028 */
1029 private EconomicMap<String, List<Binding>> getBindings(boolean includeParents, boolean flushDeferrables) {
1030 EconomicMap<String, List<Binding>> res = EconomicMap.create(Equivalence.DEFAULT);
1124 if (parent != null) {
1125 if (buf.length() != 0) {
1126 buf.append(nl);
1127 }
1128 buf.append("// parent").append(nl).append(parent);
1129 }
1130 return buf.toString();
1131 }
1132
1133 /**
1134 * Code only used in assertions. Putting this in a separate class reduces class load time.
1135 */
1136 private static class Checks {
1137 private static final int MAX_ARITY = 7;
1138 /**
1139 * The set of all {@link InvocationPlugin#apply} method signatures.
1140 */
1141 static final Class<?>[][] SIGS;
1142
1143 static {
1144 if (!Assertions.assertionsEnabled()) {
1145 throw new GraalError("%s must only be used in assertions", Checks.class.getName());
1146 }
1147 ArrayList<Class<?>[]> sigs = new ArrayList<>(MAX_ARITY);
1148 for (Method method : InvocationPlugin.class.getDeclaredMethods()) {
1149 if (!Modifier.isStatic(method.getModifiers()) && method.getName().equals("apply")) {
1150 Class<?>[] sig = method.getParameterTypes();
1151 assert sig[0] == GraphBuilderContext.class;
1152 assert sig[1] == ResolvedJavaMethod.class;
1153 assert sig[2] == InvocationPlugin.Receiver.class;
1154 assert Arrays.asList(sig).subList(3, sig.length).stream().allMatch(c -> c == ValueNode.class);
1155 while (sigs.size() < sig.length - 2) {
1156 sigs.add(null);
1157 }
1158 sigs.set(sig.length - 3, sig);
1159 }
1160 }
1161 assert sigs.indexOf(null) == -1 : format("need to add an apply() method to %s that takes %d %s arguments ", InvocationPlugin.class.getName(), sigs.indexOf(null),
1162 ValueNode.class.getSimpleName());
1163 SIGS = sigs.toArray(new Class<?>[sigs.size()][]);
1164 }
1165
1166 static boolean containsBinding(InvocationPlugins p, Type declaringType, Binding key) {
1167 String internalName = MetaUtil.toInternalName(declaringType.getTypeName());
1168 ClassPlugins classPlugins = p.registrations.get(internalName);
1169 return classPlugins != null && classPlugins.lookup(key) != null;
1170 }
1171
1172 public static boolean check(InvocationPlugins plugins, Type declaringType, Binding binding) {
1173 InvocationPlugin plugin = binding.plugin;
1174 InvocationPlugins p = plugins.parent;
1175 while (p != null) {
1176 assert !containsBinding(p, declaringType, binding) : "a plugin is already registered for " + binding;
1177 p = p.parent;
1178 }
1179 if (plugin instanceof ForeignCallPlugin || plugin instanceof GeneratedInvocationPlugin) {
1180 return true;
1181 }
1182 if (plugin instanceof MethodSubstitutionPlugin) {
1258 if (optional) {
1259 return null;
1260 }
1261 throw new GraalError("Could not resolve type " + className);
1262 }
1263 }
1264
1265 /**
1266 * Resolves a {@link Type} to a {@link Class}.
1267 *
1268 * @param type the type to resolve
1269 * @param optional if true, resolution failure returns null
1270 * @return the resolved class or null if resolution fails and {@code optional} is true
1271 */
1272 public static Class<?> resolveType(Type type, boolean optional) {
1273 if (type instanceof Class) {
1274 return (Class<?>) type;
1275 }
1276 if (type instanceof OptionalLazySymbol) {
1277 return ((OptionalLazySymbol) type).resolve();
1278 }
1279 return resolveClass(type.getTypeName(), optional);
1280 }
1281
1282 private static List<String> toInternalTypeNames(Class<?>[] types) {
1283 String[] res = new String[types.length];
1284 for (int i = 0; i < types.length; i++) {
1285 res[i] = MetaUtil.toInternalName(types[i].getTypeName());
1286 }
1287 return Arrays.asList(res);
1288 }
1289
1290 /**
1291 * Resolves a given binding to a method in a given class. If more than one method with the
1292 * parameter types matching {@code binding} is found and the return types of all the matching
1293 * methods form an inheritance chain, the one with the most specific type is returned; otherwise
1294 * {@link NoSuchMethodError} is thrown.
1295 *
1296 * @param declaringClass the class to search for a method matching {@code binding}
1297 * @return the method (if any) in {@code declaringClass} matching {@code binding}
|
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24
25 package org.graalvm.compiler.nodes.graphbuilderconf;
26
27 import static java.lang.String.format;
28 import static jdk.vm.ci.services.Services.IS_BUILDING_NATIVE_IMAGE;
29 import static jdk.vm.ci.services.Services.IS_IN_NATIVE_IMAGE;
30 import static org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.LateClassPlugins.CLOSED_LATE_CLASS_PLUGIN;
31
32 import java.lang.reflect.Constructor;
33 import java.lang.reflect.Method;
34 import java.lang.reflect.Modifier;
35 import java.lang.reflect.Type;
36 import java.util.ArrayList;
37 import java.util.Arrays;
38 import java.util.Collections;
39 import java.util.List;
40 import java.util.Map;
41
42 import jdk.internal.vm.compiler.collections.EconomicMap;
43 import jdk.internal.vm.compiler.collections.Equivalence;
44 import jdk.internal.vm.compiler.collections.MapCursor;
45 import jdk.internal.vm.compiler.collections.Pair;
46 import jdk.internal.vm.compiler.collections.UnmodifiableEconomicMap;
47 import jdk.internal.vm.compiler.collections.UnmodifiableMapCursor;
48 import org.graalvm.compiler.api.replacements.MethodSubstitution;
49 import org.graalvm.compiler.api.replacements.MethodSubstitutionRegistry;
129 public ResolvedJavaType getResolved() {
130 return resolved;
131 }
132
133 @Override
134 public String toString() {
135 return resolved.toJavaName();
136 }
137 }
138
139 /**
140 * A symbol that is lazily {@linkplain OptionalLazySymbol#resolve() resolved} to a {@link Type}.
141 */
142 static class OptionalLazySymbol implements Type {
143 private static final Class<?> MASK_NULL = OptionalLazySymbol.class;
144 private final String name;
145 private Class<?> resolved;
146
147 OptionalLazySymbol(String name) {
148 this.name = name;
149 if (IS_BUILDING_NATIVE_IMAGE) {
150 resolve();
151 }
152 }
153
154 @Override
155 public String getTypeName() {
156 return name;
157 }
158
159 /**
160 * Gets the resolved {@link Class} corresponding to this symbol or {@code null} if
161 * resolution fails.
162 */
163 public Class<?> resolve() {
164 if (!IS_IN_NATIVE_IMAGE && resolved == null) {
165 Class<?> resolvedOrNull = resolveClass(name, true);
166 resolved = resolvedOrNull == null ? MASK_NULL : resolvedOrNull;
167 }
168 return resolved == MASK_NULL ? null : resolved;
169 }
170
171 @Override
172 public String toString() {
173 return name;
174 }
175 }
176
177 /**
178 * Utility for {@linkplain InvocationPlugins#register(InvocationPlugin, Class, String, Class...)
179 * registration} of invocation plugins.
180 */
181 public static class Registration implements MethodSubstitutionRegistry {
182
183 private final InvocationPlugins plugins;
184 private final Type declaringType;
210 *
211 * @param plugins where to register the plugins
212 * @param declaringType the class declaring the methods for which plugins will be registered
213 * via this object
214 * @param methodSubstitutionBytecodeProvider provider used to get the bytecodes to parse for
215 * method substitutions
216 */
217 public Registration(InvocationPlugins plugins, Type declaringType, BytecodeProvider methodSubstitutionBytecodeProvider) {
218 this.plugins = plugins;
219 this.declaringType = declaringType;
220 this.methodSubstitutionBytecodeProvider = methodSubstitutionBytecodeProvider;
221 }
222
223 /**
224 * Creates an object for registering {@link InvocationPlugin}s for methods declared by a
225 * given class.
226 *
227 * @param plugins where to register the plugins
228 * @param declaringClassName the name of the class class declaring the methods for which
229 * plugins will be registered via this object
230 */
231 public Registration(InvocationPlugins plugins, String declaringClassName) {
232 this.plugins = plugins;
233 this.declaringType = new OptionalLazySymbol(declaringClassName);
234 this.methodSubstitutionBytecodeProvider = null;
235 }
236
237 /**
238 * Creates an object for registering {@link InvocationPlugin}s for methods declared by a
239 * given class.
240 *
241 * @param plugins where to register the plugins
242 * @param declaringClassName the name of the class class declaring the methods for which
243 * plugins will be registered via this object
244 * @param methodSubstitutionBytecodeProvider provider used to get the bytecodes to parse for
245 * method substitutions
246 */
247 public Registration(InvocationPlugins plugins, String declaringClassName, BytecodeProvider methodSubstitutionBytecodeProvider) {
248 this.plugins = plugins;
249 this.declaringType = new OptionalLazySymbol(declaringClassName);
250 this.methodSubstitutionBytecodeProvider = methodSubstitutionBytecodeProvider;
251 }
252
253 /**
254 * Configures this registration to allow or disallow overwriting of invocation plugins.
255 */
256 public Registration setAllowOverwrite(boolean allowOverwrite) {
257 this.allowOverwrite = allowOverwrite;
258 return this;
259 }
260
261 /**
262 * Registers a plugin for a method with no arguments.
263 *
454 /**
455 * Registers an invocation plugin for a given method. There must be no plugin currently
456 * registered for {@code method}.
457 *
458 * @param argumentTypes the argument types of the method. Element 0 of this array must be
459 * the {@link Class} value for {@link InvocationPlugin.Receiver} iff the method
460 * is non-static. Upon returning, element 0 will have been rewritten to
461 * {@code declaringClass}
462 */
463 public void register(InvocationPlugin plugin, String name, Type... argumentTypes) {
464 assert plugins != null : String.format("Late registrations of invocation plugins for %s is already closed", declaringType);
465 boolean isStatic = argumentTypes.length == 0 || argumentTypes[0] != InvocationPlugin.Receiver.class;
466 if (!isStatic) {
467 argumentTypes[0] = declaringType;
468 }
469
470 assert isStatic || argumentTypes[0] == declaringType;
471 Binding binding = new Binding(plugin, isStatic, name, argumentTypes);
472 bindings.add(binding);
473
474 assert IS_IN_NATIVE_IMAGE || Checks.check(this.plugins, declaringType, binding);
475 assert IS_IN_NATIVE_IMAGE || Checks.checkResolvable(false, declaringType, binding);
476 }
477
478 @Override
479 public void close() {
480 assert plugins != null : String.format("Late registrations of invocation plugins for %s is already closed", declaringType);
481 plugins.registerLate(declaringType, bindings);
482 plugins = null;
483 }
484 }
485
486 /**
487 * Associates an {@link InvocationPlugin} with the details of a method it substitutes.
488 */
489 public static class Binding {
490 /**
491 * The plugin this binding is for.
492 */
493 public final InvocationPlugin plugin;
494
495 /**
738
739 /**
740 * Determines if methods in a given class can have invocation plugins.
741 *
742 * @param declaringClass the class to test
743 */
744 public boolean canBeIntrinsified(ResolvedJavaType declaringClass) {
745 return true;
746 }
747
748 LateClassPlugins findLateClassPlugins(String internalClassName) {
749 for (LateClassPlugins lcp = lateRegistrations; lcp != null; lcp = lcp.next) {
750 if (lcp.className.equals(internalClassName)) {
751 return lcp;
752 }
753 }
754 return null;
755 }
756
757 @SuppressWarnings("serial")
758 static class InvocationPluginRegistrationError extends GraalError {
759 InvocationPluginRegistrationError(Throwable cause) {
760 super(cause);
761 }
762 }
763
764 private void flushDeferrables() {
765 if (deferredRegistrations != null) {
766 synchronized (this) {
767 if (deferredRegistrations != null) {
768 try {
769 for (Runnable deferrable : deferredRegistrations) {
770 deferrable.run();
771 }
772 deferredRegistrations = null;
773 } catch (InvocationPluginRegistrationError t) {
774 throw t;
775 } catch (Throwable t) {
776 /*
777 * Something went wrong during registration but it's possible we'll end up
778 * coming back into this code. nulling out deferredRegistrations would just
779 * cause other things to break and rerunning them would cause errors about
780 * already registered plugins, so rethrow the original exception during
781 * later invocations.
782 */
783 deferredRegistrations.clear();
784 Runnable rethrow = new Runnable() {
785 @Override
786 public void run() {
787 throw new InvocationPluginRegistrationError(t);
788 }
789 };
790 deferredRegistrations.add(rethrow);
791 rethrow.run();
792 }
793 }
794 }
795 }
796 }
797
798 private volatile EconomicMap<String, List<Binding>> testExtensions;
799
800 private static int findBinding(List<Binding> list, Binding key) {
801 for (int i = 0; i < list.size(); i++) {
802 Binding b = list.get(i);
803 if (b.isStatic == key.isStatic && b.name.equals(key.name) && b.argumentsDescriptor.equals(key.argumentsDescriptor)) {
804 return i;
805 }
806 }
807 return -1;
962 * cannot have further plugins registered.
963 */
964 public InvocationPlugins(Map<ResolvedJavaMethod, InvocationPlugin> plugins, InvocationPlugins parent) {
965 this.parent = parent;
966 this.registrations = null;
967 this.deferredRegistrations = null;
968 EconomicMap<ResolvedJavaMethod, InvocationPlugin> map = EconomicMap.create(plugins.size());
969
970 for (Map.Entry<ResolvedJavaMethod, InvocationPlugin> entry : plugins.entrySet()) {
971 map.put(entry.getKey(), entry.getValue());
972 }
973 this.resolvedRegistrations = map;
974 }
975
976 protected void register(InvocationPlugin plugin, boolean isOptional, boolean allowOverwrite, Type declaringClass, String name, Type... argumentTypes) {
977 boolean isStatic = argumentTypes.length == 0 || argumentTypes[0] != InvocationPlugin.Receiver.class;
978 if (!isStatic) {
979 argumentTypes[0] = declaringClass;
980 }
981 Binding binding = put(plugin, isStatic, allowOverwrite, declaringClass, name, argumentTypes);
982 assert IS_IN_NATIVE_IMAGE || Checks.check(this, declaringClass, binding);
983 assert IS_IN_NATIVE_IMAGE || Checks.checkResolvable(isOptional, declaringClass, binding);
984 }
985
986 /**
987 * Registers an invocation plugin for a given method. There must be no plugin currently
988 * registered for {@code method}.
989 *
990 * @param argumentTypes the argument types of the method. Element 0 of this array must be the
991 * {@link Class} value for {@link InvocationPlugin.Receiver} iff the method is
992 * non-static. Upon returning, element 0 will have been rewritten to
993 * {@code declaringClass}
994 */
995 public final void register(InvocationPlugin plugin, Type declaringClass, String name, Type... argumentTypes) {
996 register(plugin, false, false, declaringClass, name, argumentTypes);
997 }
998
999 public final void register(InvocationPlugin plugin, String declaringClass, String name, Type... argumentTypes) {
1000 register(plugin, false, false, new OptionalLazySymbol(declaringClass), name, argumentTypes);
1001 }
1002
1003 /**
1006 *
1007 * @param argumentTypes the argument types of the method. Element 0 of this array must be the
1008 * {@link Class} value for {@link InvocationPlugin.Receiver} iff the method is
1009 * non-static. Upon returning, element 0 will have been rewritten to
1010 * {@code declaringClass}
1011 */
1012 public final void registerOptional(InvocationPlugin plugin, Type declaringClass, String name, Type... argumentTypes) {
1013 register(plugin, true, false, declaringClass, name, argumentTypes);
1014 }
1015
1016 /**
1017 * Gets the plugin for a given method.
1018 *
1019 * @param method the method to lookup
1020 * @return the plugin associated with {@code method} or {@code null} if none exists
1021 */
1022 public InvocationPlugin lookupInvocation(ResolvedJavaMethod method) {
1023 if (parent != null) {
1024 InvocationPlugin plugin = parent.lookupInvocation(method);
1025 if (plugin != null) {
1026 if (IS_IN_NATIVE_IMAGE && plugin instanceof MethodSubstitutionPlugin) {
1027 // Disable method substitutions until GR-13607
1028 return null;
1029 }
1030 return plugin;
1031 }
1032 }
1033 InvocationPlugin invocationPlugin = get(method);
1034 if (IS_IN_NATIVE_IMAGE && invocationPlugin instanceof MethodSubstitutionPlugin) {
1035 // Disable method substitutions until GR-13607
1036 return null;
1037 }
1038 return invocationPlugin;
1039 }
1040
1041 /**
1042 * Gets the set of registered invocation plugins.
1043 *
1044 * @return a map from class names in {@linkplain MetaUtil#toInternalName(String) internal} form
1045 * to the invocation plugin bindings for methods in the class
1046 */
1047 public EconomicMap<String, List<Binding>> getBindings(boolean includeParents) {
1048 return getBindings(includeParents, true);
1049 }
1050
1051 /**
1052 * Gets the set of registered invocation plugins.
1053 *
1054 * @return a map from class names in {@linkplain MetaUtil#toInternalName(String) internal} form
1055 * to the invocation plugin bindings for methods in the class
1056 */
1057 private EconomicMap<String, List<Binding>> getBindings(boolean includeParents, boolean flushDeferrables) {
1058 EconomicMap<String, List<Binding>> res = EconomicMap.create(Equivalence.DEFAULT);
1152 if (parent != null) {
1153 if (buf.length() != 0) {
1154 buf.append(nl);
1155 }
1156 buf.append("// parent").append(nl).append(parent);
1157 }
1158 return buf.toString();
1159 }
1160
1161 /**
1162 * Code only used in assertions. Putting this in a separate class reduces class load time.
1163 */
1164 private static class Checks {
1165 private static final int MAX_ARITY = 7;
1166 /**
1167 * The set of all {@link InvocationPlugin#apply} method signatures.
1168 */
1169 static final Class<?>[][] SIGS;
1170
1171 static {
1172 if (!Assertions.assertionsEnabled() && !IS_BUILDING_NATIVE_IMAGE) {
1173 throw new GraalError("%s must only be used in assertions", Checks.class.getName());
1174 }
1175 ArrayList<Class<?>[]> sigs = new ArrayList<>(MAX_ARITY);
1176 if (!IS_IN_NATIVE_IMAGE) {
1177 for (Method method : InvocationPlugin.class.getDeclaredMethods()) {
1178 if (!Modifier.isStatic(method.getModifiers()) && method.getName().equals("apply")) {
1179 Class<?>[] sig = method.getParameterTypes();
1180 assert sig[0] == GraphBuilderContext.class;
1181 assert sig[1] == ResolvedJavaMethod.class;
1182 assert sig[2] == InvocationPlugin.Receiver.class;
1183 assert Arrays.asList(sig).subList(3, sig.length).stream().allMatch(c -> c == ValueNode.class);
1184 while (sigs.size() < sig.length - 2) {
1185 sigs.add(null);
1186 }
1187 sigs.set(sig.length - 3, sig);
1188 }
1189 }
1190 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),
1191 ValueNode.class.getSimpleName());
1192 }
1193 SIGS = sigs.toArray(new Class<?>[sigs.size()][]);
1194 }
1195
1196 static boolean containsBinding(InvocationPlugins p, Type declaringType, Binding key) {
1197 String internalName = MetaUtil.toInternalName(declaringType.getTypeName());
1198 ClassPlugins classPlugins = p.registrations.get(internalName);
1199 return classPlugins != null && classPlugins.lookup(key) != null;
1200 }
1201
1202 public static boolean check(InvocationPlugins plugins, Type declaringType, Binding binding) {
1203 InvocationPlugin plugin = binding.plugin;
1204 InvocationPlugins p = plugins.parent;
1205 while (p != null) {
1206 assert !containsBinding(p, declaringType, binding) : "a plugin is already registered for " + binding;
1207 p = p.parent;
1208 }
1209 if (plugin instanceof ForeignCallPlugin || plugin instanceof GeneratedInvocationPlugin) {
1210 return true;
1211 }
1212 if (plugin instanceof MethodSubstitutionPlugin) {
1288 if (optional) {
1289 return null;
1290 }
1291 throw new GraalError("Could not resolve type " + className);
1292 }
1293 }
1294
1295 /**
1296 * Resolves a {@link Type} to a {@link Class}.
1297 *
1298 * @param type the type to resolve
1299 * @param optional if true, resolution failure returns null
1300 * @return the resolved class or null if resolution fails and {@code optional} is true
1301 */
1302 public static Class<?> resolveType(Type type, boolean optional) {
1303 if (type instanceof Class) {
1304 return (Class<?>) type;
1305 }
1306 if (type instanceof OptionalLazySymbol) {
1307 return ((OptionalLazySymbol) type).resolve();
1308 }
1309 if (IS_IN_NATIVE_IMAGE) {
1310 throw new GraalError("Unresolved type in native image image:" + type.getTypeName());
1311 }
1312 return resolveClass(type.getTypeName(), optional);
1313 }
1314
1315 private static List<String> toInternalTypeNames(Class<?>[] types) {
1316 String[] res = new String[types.length];
1317 for (int i = 0; i < types.length; i++) {
1318 res[i] = MetaUtil.toInternalName(types[i].getTypeName());
1319 }
1320 return Arrays.asList(res);
1321 }
1322
1323 /**
1324 * Resolves a given binding to a method in a given class. If more than one method with the
1325 * parameter types matching {@code binding} is found and the return types of all the matching
1326 * methods form an inheritance chain, the one with the most specific type is returned; otherwise
1327 * {@link NoSuchMethodError} is thrown.
1328 *
1329 * @param declaringClass the class to search for a method matching {@code binding}
1330 * @return the method (if any) in {@code declaringClass} matching {@code binding}
|