13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23 package org.graalvm.compiler.nodes.graphbuilderconf;
24
25 import static java.lang.String.format;
26
27 import java.lang.reflect.Executable;
28 import java.lang.reflect.Method;
29 import java.lang.reflect.Modifier;
30 import java.lang.reflect.Type;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.Collections;
34 import java.util.HashMap;
35 import java.util.HashSet;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.Set;
39
40 import org.graalvm.compiler.api.replacements.MethodSubstitution;
41 import org.graalvm.compiler.api.replacements.MethodSubstitutionRegistry;
42 import org.graalvm.compiler.bytecode.BytecodeProvider;
43 import org.graalvm.compiler.debug.GraalError;
44 import org.graalvm.compiler.graph.Node;
45 import org.graalvm.compiler.graph.iterators.NodeIterable;
46 import org.graalvm.compiler.nodes.ValueNode;
47 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin.Receiver;
48
49 import jdk.vm.ci.meta.MetaAccessProvider;
50 import jdk.vm.ci.meta.MetaUtil;
51 import jdk.vm.ci.meta.ResolvedJavaMethod;
52
53 /**
54 * Manages a set of {@link InvocationPlugin}s.
55 */
56 public class InvocationPlugins {
57
58 public static class InvocationPluginReceiver implements InvocationPlugin.Receiver {
59 private final GraphBuilderContext parser;
60 private ValueNode[] args;
61 private ValueNode value;
62
63 public InvocationPluginReceiver(GraphBuilderContext parser) {
64 this.parser = parser;
65 }
66
67 @Override
243 * Registers a plugin for a method with 4 arguments.
244 *
245 * @param name the name of the method
246 * @param plugin the plugin to be registered
247 */
248 public void register4(String name, Type arg1, Type arg2, Type arg3, Type arg4, InvocationPlugin plugin) {
249 plugins.register(plugin, false, allowOverwrite, declaringType, name, arg1, arg2, arg3, arg4);
250 }
251
252 /**
253 * Registers a plugin for a method with 5 arguments.
254 *
255 * @param name the name of the method
256 * @param plugin the plugin to be registered
257 */
258 public void register5(String name, Type arg1, Type arg2, Type arg3, Type arg4, Type arg5, InvocationPlugin plugin) {
259 plugins.register(plugin, false, allowOverwrite, declaringType, name, arg1, arg2, arg3, arg4, arg5);
260 }
261
262 /**
263 * Registers a plugin for an optional method with no arguments.
264 *
265 * @param name the name of the method
266 * @param plugin the plugin to be registered
267 */
268 public void registerOptional0(String name, InvocationPlugin plugin) {
269 plugins.register(plugin, true, allowOverwrite, declaringType, name);
270 }
271
272 /**
273 * Registers a plugin for an optional method with 1 argument.
274 *
275 * @param name the name of the method
276 * @param plugin the plugin to be registered
277 */
278 public void registerOptional1(String name, Type arg, InvocationPlugin plugin) {
279 plugins.register(plugin, true, allowOverwrite, declaringType, name, arg);
280 }
281
282 /**
320 * {@code declaringClass}
321 */
322 @Override
323 public void registerMethodSubstitution(Class<?> substituteDeclaringClass, String name, Type... argumentTypes) {
324 registerMethodSubstitution(substituteDeclaringClass, name, name, argumentTypes);
325 }
326
327 /**
328 * Registers a plugin that implements a method based on the bytecode of a substitute method.
329 *
330 * @param substituteDeclaringClass the class declaring the substitute method
331 * @param name the name of both the original method
332 * @param substituteName the name of the substitute method
333 * @param argumentTypes the argument types of the method. Element 0 of this array must be
334 * the {@link Class} value for {@link InvocationPlugin.Receiver} iff the method
335 * is non-static. Upon returning, element 0 will have been rewritten to
336 * {@code declaringClass}
337 */
338 @Override
339 public void registerMethodSubstitution(Class<?> substituteDeclaringClass, String name, String substituteName, Type... argumentTypes) {
340 assert methodSubstitutionBytecodeProvider != null : "Registration used for method substitutions requires a non-null methodSubstitutionBytecodeProvider";
341 MethodSubstitutionPlugin plugin = new MethodSubstitutionPlugin(methodSubstitutionBytecodeProvider, substituteDeclaringClass, substituteName, argumentTypes);
342 plugins.register(plugin, false, allowOverwrite, declaringType, name, argumentTypes);
343 }
344 }
345
346 /**
347 * Key for a {@linkplain ClassPlugins#entries resolved} plugin registration. Due to the
348 * possibility of class redefinition, we cannot directly use {@link ResolvedJavaMethod}s as
349 * keys. A {@link ResolvedJavaMethod} implementation might implement {@code equals()} and
350 * {@code hashCode()} based on internal representation subject to change by class redefinition.
351 */
352 static final class ResolvedJavaMethodKey {
353 private final ResolvedJavaMethod method;
354
355 ResolvedJavaMethodKey(ResolvedJavaMethod method) {
356 this.method = method;
357 }
358
359 @Override
360 public boolean equals(Object obj) {
361 if (obj instanceof ResolvedJavaMethodKey) {
362 ResolvedJavaMethodKey that = (ResolvedJavaMethodKey) obj;
363 if (this.method.isStatic() == that.method.isStatic()) {
477 }
478 throw new InternalError(e);
479 }
480 }
481
482 @Override
483 public String toString() {
484 StringBuilder sb = new StringBuilder(name).append('(');
485 for (Type p : argumentTypes) {
486 if (sb.charAt(sb.length() - 1) != '(') {
487 sb.append(", ");
488 }
489 sb.append(p.getTypeName());
490 }
491 return sb.append(')').toString();
492 }
493 }
494
495 private final MetaAccessProvider metaAccess;
496
497 private final Map<String, ClassPlugins> registrations = new HashMap<>();
498
499 /**
500 * Deferred registrations as well as guard for initialization. The guard uses double-checked
501 * locking which is why this field is {@code volatile}.
502 */
503 private volatile List<Runnable> deferredRegistrations = new ArrayList<>();
504
505 /**
506 * Adds a {@link Runnable} for doing registration deferred until the first time
507 * {@link #get(ResolvedJavaMethod)} or {@link #closeRegistration()} is called on this object.
508 */
509 public void defer(Runnable deferrable) {
510 assert deferredRegistrations != null : "registration is closed";
511 deferredRegistrations.add(deferrable);
512 }
513
514 /**
515 * Per-class invocation plugins.
516 */
517 protected static class ClassPlugins {
518 private final Type declaringType;
519
520 private final List<MethodKey> registrations = new ArrayList<>();
521
522 public ClassPlugins(Type declaringClass) {
523 this.declaringType = declaringClass;
524 }
525
526 /**
527 * Entry map that is initialized upon first call to {@link #get(ResolvedJavaMethod)}.
528 *
529 * Note: this must be volatile as threads may race to initialize it.
530 */
531 private volatile Map<ResolvedJavaMethodKey, InvocationPlugin> entries;
532
533 void initializeMap() {
534 if (!isClosed()) {
535 if (registrations.isEmpty()) {
536 entries = Collections.emptyMap();
537 } else {
538 Class<?> declaringClass = resolveType(declaringType, true);
539 if (declaringClass == null) {
540 // An optional type that could not be resolved
541 entries = Collections.emptyMap();
542 } else {
543 Map<ResolvedJavaMethodKey, InvocationPlugin> newEntries = new HashMap<>();
544 for (MethodKey methodKey : registrations) {
545 ResolvedJavaMethod m = methodKey.resolve(declaringClass);
546 if (m != null) {
547 newEntries.put(new ResolvedJavaMethodKey(m), methodKey.value);
548 if (entries != null) {
549 // Another thread finished initializing entries first
550 return;
551 }
552 }
553 }
554 entries = newEntries;
555 }
556 }
557 }
558 }
559
560 public InvocationPlugin get(ResolvedJavaMethod method) {
561 if (!isClosed()) {
562 initializeMap();
563 }
564 return entries.get(new ResolvedJavaMethodKey(method));
565 }
566
567 public void register(MethodKey methodKey, boolean allowOverwrite) {
568 assert !isClosed() : "registration is closed: " + methodKey + " " + Arrays.toString(entries.keySet().toArray());
569 if (allowOverwrite) {
570 int index = registrations.indexOf(methodKey);
571 if (index >= 0) {
572 registrations.set(index, methodKey);
573 return;
574 }
575 } else {
576 assert !registrations.contains(methodKey) : "a value is already registered for " + declaringType + "." + methodKey;
577 }
578 registrations.add(methodKey);
579 }
580
581 public boolean isClosed() {
582 return entries != null;
583 }
584 }
585
586 /**
587 * Adds an entry to this map for a specified method.
588 *
622 InvocationPlugin get(ResolvedJavaMethod method) {
623 flushDeferrables();
624 String internalName = method.getDeclaringClass().getName();
625 ClassPlugins classPlugins = registrations.get(internalName);
626 if (classPlugins != null) {
627 return classPlugins.get(method);
628 }
629 return null;
630 }
631
632 private void flushDeferrables() {
633 if (deferredRegistrations != null) {
634 synchronized (this) {
635 if (deferredRegistrations != null) {
636 for (Runnable deferrable : deferredRegistrations) {
637 deferrable.run();
638 }
639 deferredRegistrations = null;
640 }
641 }
642 for (Map.Entry<String, ClassPlugins> e : registrations.entrySet()) {
643 e.getValue().initializeMap();
644 }
645 }
646 }
647
648 /**
649 * Disallows new registrations of new plugins, and creates the internal tables for method
650 * lookup.
651 */
652 public void closeRegistration() {
653 flushDeferrables();
654 for (Map.Entry<String, ClassPlugins> e : registrations.entrySet()) {
655 e.getValue().initializeMap();
656 }
657 }
658
659 public int size() {
660 return registrations.size();
661 }
662
663 /**
664 * The plugins {@linkplain #lookupInvocation(ResolvedJavaMethod) searched} before searching in
665 * this object.
666 */
667 protected final InvocationPlugins parent;
668
669 private InvocationPlugins(InvocationPlugins parent, MetaAccessProvider metaAccess) {
670 this.metaAccess = metaAccess;
671 InvocationPlugins p = parent;
672 this.parent = p;
673 }
674
675 /**
677 */
678 public InvocationPlugins(InvocationPlugins parent) {
679 this(parent, parent.getMetaAccess());
680 }
681
682 public InvocationPlugins(Map<ResolvedJavaMethod, InvocationPlugin> plugins, InvocationPlugins parent, MetaAccessProvider metaAccess) {
683 this.metaAccess = metaAccess;
684 this.parent = parent;
685
686 this.deferredRegistrations = null;
687
688 for (Map.Entry<ResolvedJavaMethod, InvocationPlugin> entry : plugins.entrySet()) {
689 ResolvedJavaMethod method = entry.getKey();
690 InvocationPlugin plugin = entry.getValue();
691
692 String internalName = method.getDeclaringClass().getName();
693 ClassPlugins classPlugins = registrations.get(internalName);
694 if (classPlugins == null) {
695 classPlugins = new ClassPlugins(null);
696 registrations.put(internalName, classPlugins);
697 classPlugins.entries = new HashMap<>();
698 }
699
700 classPlugins.entries.put(new ResolvedJavaMethodKey(method), plugin);
701 }
702 }
703
704 public MetaAccessProvider getMetaAccess() {
705 return metaAccess;
706 }
707
708 public InvocationPlugins(MetaAccessProvider metaAccess) {
709 this(null, metaAccess);
710 }
711
712 protected void register(InvocationPlugin plugin, boolean isOptional, boolean allowOverwrite, Type declaringClass, String name, Type... argumentTypes) {
713 boolean isStatic = argumentTypes.length == 0 || argumentTypes[0] != InvocationPlugin.Receiver.class;
714 if (!isStatic) {
715 argumentTypes[0] = declaringClass;
716 }
717 MethodKey methodKey = put(plugin, isStatic, isOptional, allowOverwrite, declaringClass, name, argumentTypes);
751 /**
752 * Gets the plugin for a given method.
753 *
754 * @param method the method to lookup
755 * @return the plugin associated with {@code method} or {@code null} if none exists
756 */
757 public InvocationPlugin lookupInvocation(ResolvedJavaMethod method) {
758 if (parent != null) {
759 InvocationPlugin plugin = parent.lookupInvocation(method);
760 if (plugin != null) {
761 return plugin;
762 }
763 }
764 return get(method);
765 }
766
767 /**
768 * Gets the set of methods for which invocation plugins have been registered. Once this method
769 * is called, no further registrations can be made.
770 */
771 public Set<ResolvedJavaMethod> getMethods() {
772 Set<ResolvedJavaMethod> res = new HashSet<>();
773 if (parent != null) {
774 res.addAll(parent.getMethods());
775 }
776 flushDeferrables();
777 for (ClassPlugins cp : registrations.values()) {
778 for (ResolvedJavaMethodKey key : cp.entries.keySet()) {
779 res.add(key.method);
780 }
781 }
782 return res;
783 }
784
785 /**
786 * Gets the invocation plugins {@linkplain #lookupInvocation(ResolvedJavaMethod) searched}
787 * before searching in this object.
788 */
789 public InvocationPlugins getParent() {
790 return parent;
791 }
792
793 @Override
794 public String toString() {
795 StringBuilder buf = new StringBuilder();
796 registrations.forEach((name, cp) -> buf.append(name).append('.').append(cp).append(", "));
797 String s = buf.toString();
798 if (buf.length() != 0) {
799 s = s.substring(buf.length() - ", ".length());
800 }
801 return s + " / parent: " + this.parent;
802 }
803
804 private static class Checker {
805 private static final int MAX_ARITY = 5;
806 /**
807 * The set of all {@link InvocationPlugin#apply} method signatures.
808 */
809 static final Class<?>[][] SIGS;
810
811 static {
812 ArrayList<Class<?>[]> sigs = new ArrayList<>(MAX_ARITY);
813 for (Method method : InvocationPlugin.class.getDeclaredMethods()) {
814 if (!Modifier.isStatic(method.getModifiers()) && method.getName().equals("apply")) {
815 Class<?>[] sig = method.getParameterTypes();
816 assert sig[0] == GraphBuilderContext.class;
817 assert sig[1] == ResolvedJavaMethod.class;
818 assert sig[2] == InvocationPlugin.Receiver.class;
819 assert Arrays.asList(sig).subList(3, sig.length).stream().allMatch(c -> c == ValueNode.class);
820 while (sigs.size() < sig.length - 2) {
821 sigs.add(null);
822 }
823 sigs.set(sig.length - 3, sig);
824 }
825 }
|
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23 package org.graalvm.compiler.nodes.graphbuilderconf;
24
25 import static java.lang.String.format;
26
27 import java.lang.reflect.Executable;
28 import java.lang.reflect.Method;
29 import java.lang.reflect.Modifier;
30 import java.lang.reflect.Type;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.List;
34 import java.util.Map;
35 import org.graalvm.compiler.api.replacements.MethodSubstitution;
36 import org.graalvm.compiler.api.replacements.MethodSubstitutionRegistry;
37 import org.graalvm.compiler.bytecode.BytecodeProvider;
38 import org.graalvm.compiler.debug.GraalError;
39 import org.graalvm.compiler.graph.Node;
40 import org.graalvm.compiler.graph.iterators.NodeIterable;
41 import org.graalvm.compiler.nodes.ValueNode;
42 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin.Receiver;
43 import org.graalvm.util.Equivalence;
44 import org.graalvm.util.EconomicMap;
45 import org.graalvm.util.EconomicSet;
46 import org.graalvm.util.UnmodifiableMapCursor;
47
48 import jdk.vm.ci.meta.MetaAccessProvider;
49 import jdk.vm.ci.meta.MetaUtil;
50 import jdk.vm.ci.meta.ResolvedJavaMethod;
51
52 /**
53 * Manages a set of {@link InvocationPlugin}s.
54 */
55 public class InvocationPlugins {
56
57 public static class InvocationPluginReceiver implements InvocationPlugin.Receiver {
58 private final GraphBuilderContext parser;
59 private ValueNode[] args;
60 private ValueNode value;
61
62 public InvocationPluginReceiver(GraphBuilderContext parser) {
63 this.parser = parser;
64 }
65
66 @Override
242 * Registers a plugin for a method with 4 arguments.
243 *
244 * @param name the name of the method
245 * @param plugin the plugin to be registered
246 */
247 public void register4(String name, Type arg1, Type arg2, Type arg3, Type arg4, InvocationPlugin plugin) {
248 plugins.register(plugin, false, allowOverwrite, declaringType, name, arg1, arg2, arg3, arg4);
249 }
250
251 /**
252 * Registers a plugin for a method with 5 arguments.
253 *
254 * @param name the name of the method
255 * @param plugin the plugin to be registered
256 */
257 public void register5(String name, Type arg1, Type arg2, Type arg3, Type arg4, Type arg5, InvocationPlugin plugin) {
258 plugins.register(plugin, false, allowOverwrite, declaringType, name, arg1, arg2, arg3, arg4, arg5);
259 }
260
261 /**
262 * Registers a plugin for a method with 6 arguments.
263 *
264 * @param name the name of the method
265 * @param plugin the plugin to be registered
266 */
267 public void register6(String name, Type arg1, Type arg2, Type arg3, Type arg4, Type arg5, Type arg6, InvocationPlugin plugin) {
268 plugins.register(plugin, false, allowOverwrite, declaringType, name, arg1, arg2, arg3, arg4, arg5, arg6);
269 }
270
271 /**
272 * Registers a plugin for a method with 7 arguments.
273 *
274 * @param name the name of the method
275 * @param plugin the plugin to be registered
276 */
277 public void register7(String name, Type arg1, Type arg2, Type arg3, Type arg4, Type arg5, Type arg6, Type arg7, InvocationPlugin plugin) {
278 plugins.register(plugin, false, allowOverwrite, declaringType, name, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
279 }
280
281 /**
282 * Registers a plugin for an optional method with no arguments.
283 *
284 * @param name the name of the method
285 * @param plugin the plugin to be registered
286 */
287 public void registerOptional0(String name, InvocationPlugin plugin) {
288 plugins.register(plugin, true, allowOverwrite, declaringType, name);
289 }
290
291 /**
292 * Registers a plugin for an optional method with 1 argument.
293 *
294 * @param name the name of the method
295 * @param plugin the plugin to be registered
296 */
297 public void registerOptional1(String name, Type arg, InvocationPlugin plugin) {
298 plugins.register(plugin, true, allowOverwrite, declaringType, name, arg);
299 }
300
301 /**
339 * {@code declaringClass}
340 */
341 @Override
342 public void registerMethodSubstitution(Class<?> substituteDeclaringClass, String name, Type... argumentTypes) {
343 registerMethodSubstitution(substituteDeclaringClass, name, name, argumentTypes);
344 }
345
346 /**
347 * Registers a plugin that implements a method based on the bytecode of a substitute method.
348 *
349 * @param substituteDeclaringClass the class declaring the substitute method
350 * @param name the name of both the original method
351 * @param substituteName the name of the substitute method
352 * @param argumentTypes the argument types of the method. Element 0 of this array must be
353 * the {@link Class} value for {@link InvocationPlugin.Receiver} iff the method
354 * is non-static. Upon returning, element 0 will have been rewritten to
355 * {@code declaringClass}
356 */
357 @Override
358 public void registerMethodSubstitution(Class<?> substituteDeclaringClass, String name, String substituteName, Type... argumentTypes) {
359 MethodSubstitutionPlugin plugin = createMethodSubstitution(substituteDeclaringClass, substituteName, argumentTypes);
360 plugins.register(plugin, false, allowOverwrite, declaringType, name, argumentTypes);
361 }
362
363 public MethodSubstitutionPlugin createMethodSubstitution(Class<?> substituteDeclaringClass, String substituteName, Type... argumentTypes) {
364 assert methodSubstitutionBytecodeProvider != null : "Registration used for method substitutions requires a non-null methodSubstitutionBytecodeProvider";
365 MethodSubstitutionPlugin plugin = new MethodSubstitutionPlugin(methodSubstitutionBytecodeProvider, substituteDeclaringClass, substituteName, argumentTypes);
366 return plugin;
367 }
368
369 }
370
371 /**
372 * Key for a {@linkplain ClassPlugins#entries resolved} plugin registration. Due to the
373 * possibility of class redefinition, we cannot directly use {@link ResolvedJavaMethod}s as
374 * keys. A {@link ResolvedJavaMethod} implementation might implement {@code equals()} and
375 * {@code hashCode()} based on internal representation subject to change by class redefinition.
376 */
377 static final class ResolvedJavaMethodKey {
378 private final ResolvedJavaMethod method;
379
380 ResolvedJavaMethodKey(ResolvedJavaMethod method) {
381 this.method = method;
382 }
383
384 @Override
385 public boolean equals(Object obj) {
386 if (obj instanceof ResolvedJavaMethodKey) {
387 ResolvedJavaMethodKey that = (ResolvedJavaMethodKey) obj;
388 if (this.method.isStatic() == that.method.isStatic()) {
502 }
503 throw new InternalError(e);
504 }
505 }
506
507 @Override
508 public String toString() {
509 StringBuilder sb = new StringBuilder(name).append('(');
510 for (Type p : argumentTypes) {
511 if (sb.charAt(sb.length() - 1) != '(') {
512 sb.append(", ");
513 }
514 sb.append(p.getTypeName());
515 }
516 return sb.append(')').toString();
517 }
518 }
519
520 private final MetaAccessProvider metaAccess;
521
522 private final EconomicMap<String, ClassPlugins> registrations = EconomicMap.create(Equivalence.DEFAULT);
523
524 /**
525 * Deferred registrations as well as guard for initialization. The guard uses double-checked
526 * locking which is why this field is {@code volatile}.
527 */
528 private volatile List<Runnable> deferredRegistrations = new ArrayList<>();
529
530 /**
531 * Adds a {@link Runnable} for doing registration deferred until the first time
532 * {@link #get(ResolvedJavaMethod)} or {@link #closeRegistration()} is called on this object.
533 */
534 public void defer(Runnable deferrable) {
535 assert deferredRegistrations != null : "registration is closed";
536 deferredRegistrations.add(deferrable);
537 }
538
539 /**
540 * Per-class invocation plugins.
541 */
542 protected static class ClassPlugins {
543 private final Type declaringType;
544
545 private final List<MethodKey> registrations = new ArrayList<>();
546
547 public ClassPlugins(Type declaringClass) {
548 this.declaringType = declaringClass;
549 }
550
551 /**
552 * Entry map that is initialized upon first call to {@link #get(ResolvedJavaMethod)}.
553 *
554 * Note: this must be volatile as threads may race to initialize it.
555 */
556 private volatile EconomicMap<ResolvedJavaMethodKey, InvocationPlugin> entries;
557
558 void initializeMap() {
559 if (!isClosed()) {
560 if (registrations.isEmpty()) {
561 entries = EconomicMap.create(Equivalence.DEFAULT);
562 } else {
563 Class<?> declaringClass = resolveType(declaringType, true);
564 if (declaringClass == null) {
565 // An optional type that could not be resolved
566 entries = EconomicMap.create(Equivalence.DEFAULT);
567 } else {
568 EconomicMap<ResolvedJavaMethodKey, InvocationPlugin> newEntries = EconomicMap.create(Equivalence.DEFAULT);
569 for (MethodKey methodKey : registrations) {
570 ResolvedJavaMethod m = methodKey.resolve(declaringClass);
571 if (m != null) {
572 newEntries.put(new ResolvedJavaMethodKey(m), methodKey.value);
573 if (entries != null) {
574 // Another thread finished initializing entries first
575 return;
576 }
577 }
578 }
579 entries = newEntries;
580 }
581 }
582 }
583 }
584
585 public InvocationPlugin get(ResolvedJavaMethod method) {
586 if (!isClosed()) {
587 initializeMap();
588 }
589 return entries.get(new ResolvedJavaMethodKey(method));
590 }
591
592 public void register(MethodKey methodKey, boolean allowOverwrite) {
593 assert !isClosed() : "registration is closed: " + methodKey;
594 if (allowOverwrite) {
595 int index = registrations.indexOf(methodKey);
596 if (index >= 0) {
597 registrations.set(index, methodKey);
598 return;
599 }
600 } else {
601 assert !registrations.contains(methodKey) : "a value is already registered for " + declaringType + "." + methodKey;
602 }
603 registrations.add(methodKey);
604 }
605
606 public boolean isClosed() {
607 return entries != null;
608 }
609 }
610
611 /**
612 * Adds an entry to this map for a specified method.
613 *
647 InvocationPlugin get(ResolvedJavaMethod method) {
648 flushDeferrables();
649 String internalName = method.getDeclaringClass().getName();
650 ClassPlugins classPlugins = registrations.get(internalName);
651 if (classPlugins != null) {
652 return classPlugins.get(method);
653 }
654 return null;
655 }
656
657 private void flushDeferrables() {
658 if (deferredRegistrations != null) {
659 synchronized (this) {
660 if (deferredRegistrations != null) {
661 for (Runnable deferrable : deferredRegistrations) {
662 deferrable.run();
663 }
664 deferredRegistrations = null;
665 }
666 }
667 for (ClassPlugins e : registrations.getValues()) {
668 e.initializeMap();
669 }
670 }
671 }
672
673 /**
674 * Disallows new registrations of new plugins, and creates the internal tables for method
675 * lookup.
676 */
677 public void closeRegistration() {
678 flushDeferrables();
679 for (ClassPlugins e : registrations.getValues()) {
680 e.initializeMap();
681 }
682 }
683
684 public int size() {
685 return registrations.size();
686 }
687
688 /**
689 * The plugins {@linkplain #lookupInvocation(ResolvedJavaMethod) searched} before searching in
690 * this object.
691 */
692 protected final InvocationPlugins parent;
693
694 private InvocationPlugins(InvocationPlugins parent, MetaAccessProvider metaAccess) {
695 this.metaAccess = metaAccess;
696 InvocationPlugins p = parent;
697 this.parent = p;
698 }
699
700 /**
702 */
703 public InvocationPlugins(InvocationPlugins parent) {
704 this(parent, parent.getMetaAccess());
705 }
706
707 public InvocationPlugins(Map<ResolvedJavaMethod, InvocationPlugin> plugins, InvocationPlugins parent, MetaAccessProvider metaAccess) {
708 this.metaAccess = metaAccess;
709 this.parent = parent;
710
711 this.deferredRegistrations = null;
712
713 for (Map.Entry<ResolvedJavaMethod, InvocationPlugin> entry : plugins.entrySet()) {
714 ResolvedJavaMethod method = entry.getKey();
715 InvocationPlugin plugin = entry.getValue();
716
717 String internalName = method.getDeclaringClass().getName();
718 ClassPlugins classPlugins = registrations.get(internalName);
719 if (classPlugins == null) {
720 classPlugins = new ClassPlugins(null);
721 registrations.put(internalName, classPlugins);
722 classPlugins.entries = EconomicMap.create(Equivalence.DEFAULT);
723 }
724
725 classPlugins.entries.put(new ResolvedJavaMethodKey(method), plugin);
726 }
727 }
728
729 public MetaAccessProvider getMetaAccess() {
730 return metaAccess;
731 }
732
733 public InvocationPlugins(MetaAccessProvider metaAccess) {
734 this(null, metaAccess);
735 }
736
737 protected void register(InvocationPlugin plugin, boolean isOptional, boolean allowOverwrite, Type declaringClass, String name, Type... argumentTypes) {
738 boolean isStatic = argumentTypes.length == 0 || argumentTypes[0] != InvocationPlugin.Receiver.class;
739 if (!isStatic) {
740 argumentTypes[0] = declaringClass;
741 }
742 MethodKey methodKey = put(plugin, isStatic, isOptional, allowOverwrite, declaringClass, name, argumentTypes);
776 /**
777 * Gets the plugin for a given method.
778 *
779 * @param method the method to lookup
780 * @return the plugin associated with {@code method} or {@code null} if none exists
781 */
782 public InvocationPlugin lookupInvocation(ResolvedJavaMethod method) {
783 if (parent != null) {
784 InvocationPlugin plugin = parent.lookupInvocation(method);
785 if (plugin != null) {
786 return plugin;
787 }
788 }
789 return get(method);
790 }
791
792 /**
793 * Gets the set of methods for which invocation plugins have been registered. Once this method
794 * is called, no further registrations can be made.
795 */
796 public EconomicSet<ResolvedJavaMethod> getMethods() {
797 EconomicSet<ResolvedJavaMethod> res = EconomicSet.create(Equivalence.DEFAULT);
798 if (parent != null) {
799 res.addAll(parent.getMethods());
800 }
801 flushDeferrables();
802 for (ClassPlugins cp : registrations.getValues()) {
803 for (ResolvedJavaMethodKey key : cp.entries.getKeys()) {
804 res.add(key.method);
805 }
806 }
807 return res;
808 }
809
810 /**
811 * Gets the invocation plugins {@linkplain #lookupInvocation(ResolvedJavaMethod) searched}
812 * before searching in this object.
813 */
814 public InvocationPlugins getParent() {
815 return parent;
816 }
817
818 @Override
819 public String toString() {
820 StringBuilder buf = new StringBuilder();
821 UnmodifiableMapCursor<String, ClassPlugins> entries = registrations.getEntries();
822 while (entries.advance()) {
823 buf.append(entries.getKey()).append('.').append(entries.getValue()).append(", ");
824 }
825
826 String s = buf.toString();
827 if (buf.length() != 0) {
828 s = s.substring(buf.length() - ", ".length());
829 }
830 return s + " / parent: " + this.parent;
831 }
832
833 private static class Checker {
834 private static final int MAX_ARITY = 7;
835 /**
836 * The set of all {@link InvocationPlugin#apply} method signatures.
837 */
838 static final Class<?>[][] SIGS;
839
840 static {
841 ArrayList<Class<?>[]> sigs = new ArrayList<>(MAX_ARITY);
842 for (Method method : InvocationPlugin.class.getDeclaredMethods()) {
843 if (!Modifier.isStatic(method.getModifiers()) && method.getName().equals("apply")) {
844 Class<?>[] sig = method.getParameterTypes();
845 assert sig[0] == GraphBuilderContext.class;
846 assert sig[1] == ResolvedJavaMethod.class;
847 assert sig[2] == InvocationPlugin.Receiver.class;
848 assert Arrays.asList(sig).subList(3, sig.length).stream().allMatch(c -> c == ValueNode.class);
849 while (sigs.size() < sig.length - 2) {
850 sigs.add(null);
851 }
852 sigs.set(sig.length - 3, sig);
853 }
854 }
|