1 /*
2  * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 
25 package org.graalvm.compiler.nodes.graphbuilderconf;
26 
27 import java.lang.invoke.MethodHandle;
28 import java.lang.reflect.Method;
29 
30 import org.graalvm.compiler.debug.GraalError;
31 import org.graalvm.compiler.nodes.Invoke;
32 import org.graalvm.compiler.nodes.ValueNode;
33 import org.graalvm.compiler.nodes.type.StampTool;
34 
35 import jdk.vm.ci.meta.MetaAccessProvider;
36 import jdk.vm.ci.meta.ResolvedJavaMethod;
37 
38 /**
39  * Plugin for handling a specific method invocation.
40  */
41 public interface InvocationPlugin extends GraphBuilderPlugin {
42 
43     /**
44      * The receiver in a non-static method. The class literal for this interface must be used with
45      * {@link InvocationPlugins#put(InvocationPlugin, boolean, boolean, Class, String, Class...)} to
46      * denote the receiver argument for such a non-static method.
47      */
48     public interface Receiver {
49         /**
50          * Gets the receiver value, null checking it first if necessary.
51          *
52          * @return the receiver value with a {@linkplain StampTool#isPointerNonNull(ValueNode)
53          *         non-null} stamp
54          */
get()55         default ValueNode get() {
56             return get(true);
57         }
58 
59         /**
60          * Gets the receiver value, optionally null checking it first if necessary.
61          */
get(boolean performNullCheck)62         ValueNode get(boolean performNullCheck);
63 
64         /**
65          * Determines if the receiver is constant.
66          */
isConstant()67         default boolean isConstant() {
68             return false;
69         }
70     }
71 
72     /**
73      * Determines if this plugin is for a method with a polymorphic signature (e.g.
74      * {@link MethodHandle#invokeExact(Object...)}).
75      */
isSignaturePolymorphic()76     default boolean isSignaturePolymorphic() {
77         return false;
78     }
79 
80     /**
81      * Determines if this plugin can only be used when inlining the method is it associated with.
82      * That is, this plugin cannot be used when the associated method is the compilation root.
83      */
inlineOnly()84     default boolean inlineOnly() {
85         return isSignaturePolymorphic();
86     }
87 
88     /**
89      * Determines if this plugin only decorates the method is it associated with. That is, it
90      * inserts nodes prior to the invocation (e.g. some kind of marker nodes) but still expects the
91      * parser to process the invocation further.
92      */
isDecorator()93     default boolean isDecorator() {
94         return false;
95     }
96 
97     /**
98      * Handles invocation of a signature polymorphic method.
99      *
100      * @param receiver access to the receiver, {@code null} if {@code targetMethod} is static
101      * @param argsIncludingReceiver all arguments to the invocation include the raw receiver in
102      *            position 0 if {@code targetMethod} is not static
103      * @see #execute
104      */
applyPolymorphic(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode... argsIncludingReceiver)105     default boolean applyPolymorphic(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode... argsIncludingReceiver) {
106         return defaultHandler(b, targetMethod, receiver, argsIncludingReceiver);
107     }
108 
109     /**
110      * @see #execute
111      */
apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver)112     default boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver) {
113         return defaultHandler(b, targetMethod, receiver);
114     }
115 
116     /**
117      * @see #execute
118      */
apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode arg)119     default boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode arg) {
120         return defaultHandler(b, targetMethod, receiver, arg);
121     }
122 
123     /**
124      * @see #execute
125      */
apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode arg1, ValueNode arg2)126     default boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode arg1, ValueNode arg2) {
127         return defaultHandler(b, targetMethod, receiver, arg1, arg2);
128     }
129 
130     /**
131      * @see #execute
132      */
apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode arg1, ValueNode arg2, ValueNode arg3)133     default boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode arg1, ValueNode arg2, ValueNode arg3) {
134         return defaultHandler(b, targetMethod, receiver, arg1, arg2, arg3);
135     }
136 
137     /**
138      * @see #execute
139      */
apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode arg1, ValueNode arg2, ValueNode arg3, ValueNode arg4)140     default boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode arg1, ValueNode arg2, ValueNode arg3, ValueNode arg4) {
141         return defaultHandler(b, targetMethod, receiver, arg1, arg2, arg3, arg4);
142     }
143 
144     /**
145      * @see #execute
146      */
apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode arg1, ValueNode arg2, ValueNode arg3, ValueNode arg4, ValueNode arg5)147     default boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode arg1, ValueNode arg2, ValueNode arg3, ValueNode arg4, ValueNode arg5) {
148         return defaultHandler(b, targetMethod, receiver, arg1, arg2, arg3, arg4, arg5);
149     }
150 
151     /**
152      * @see #execute
153      */
apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode arg1, ValueNode arg2, ValueNode arg3, ValueNode arg4, ValueNode arg5, ValueNode arg6)154     default boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode arg1, ValueNode arg2, ValueNode arg3, ValueNode arg4, ValueNode arg5,
155                     ValueNode arg6) {
156         return defaultHandler(b, targetMethod, receiver, arg1, arg2, arg3, arg4, arg5, arg6);
157     }
158 
159     /**
160      * @see #execute
161      */
apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode arg1, ValueNode arg2, ValueNode arg3, ValueNode arg4, ValueNode arg5, ValueNode arg6, ValueNode arg7)162     default boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode arg1, ValueNode arg2, ValueNode arg3, ValueNode arg4, ValueNode arg5,
163                     ValueNode arg6, ValueNode arg7) {
164         return defaultHandler(b, targetMethod, receiver, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
165     }
166 
167     /**
168      * Executes this plugin against a set of invocation arguments.
169      *
170      * The default implementation in {@link InvocationPlugin} dispatches to the {@code apply(...)}
171      * method that matches the number of arguments or to {@link #applyPolymorphic} if {@code plugin}
172      * is {@linkplain #isSignaturePolymorphic() signature polymorphic}.
173      *
174      * @param targetMethod the method for which this plugin is being applied
175      * @param receiver access to the receiver, {@code null} if {@code targetMethod} is static
176      * @param argsIncludingReceiver all arguments to the invocation include the receiver in position
177      *            0 if {@code targetMethod} is not static
178      * @return {@code true} if this plugin handled the invocation of {@code targetMethod}
179      *         {@code false} if the graph builder should process the invoke further (e.g., by
180      *         inlining it or creating an {@link Invoke} node). A plugin that does not handle an
181      *         invocation must not modify the graph being constructed unless it is a
182      *         {@linkplain InvocationPlugin#isDecorator() decorator}.
183      */
execute(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode[] argsIncludingReceiver)184     default boolean execute(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode[] argsIncludingReceiver) {
185         if (isSignaturePolymorphic()) {
186             return applyPolymorphic(b, targetMethod, receiver, argsIncludingReceiver);
187         } else if (receiver != null) {
188             assert !targetMethod.isStatic();
189             assert argsIncludingReceiver.length > 0;
190             if (argsIncludingReceiver.length == 1) {
191                 return apply(b, targetMethod, receiver);
192             } else if (argsIncludingReceiver.length == 2) {
193                 return apply(b, targetMethod, receiver, argsIncludingReceiver[1]);
194             } else if (argsIncludingReceiver.length == 3) {
195                 return apply(b, targetMethod, receiver, argsIncludingReceiver[1], argsIncludingReceiver[2]);
196             } else if (argsIncludingReceiver.length == 4) {
197                 return apply(b, targetMethod, receiver, argsIncludingReceiver[1], argsIncludingReceiver[2], argsIncludingReceiver[3]);
198             } else if (argsIncludingReceiver.length == 5) {
199                 return apply(b, targetMethod, receiver, argsIncludingReceiver[1], argsIncludingReceiver[2], argsIncludingReceiver[3], argsIncludingReceiver[4]);
200             } else if (argsIncludingReceiver.length == 6) {
201                 return apply(b, targetMethod, receiver, argsIncludingReceiver[1], argsIncludingReceiver[2], argsIncludingReceiver[3], argsIncludingReceiver[4], argsIncludingReceiver[5]);
202             } else if (argsIncludingReceiver.length == 7) {
203                 return apply(b, targetMethod, receiver, argsIncludingReceiver[1], argsIncludingReceiver[2], argsIncludingReceiver[3], argsIncludingReceiver[4], argsIncludingReceiver[5],
204                                 argsIncludingReceiver[6]);
205             } else {
206                 return defaultHandler(b, targetMethod, receiver, argsIncludingReceiver);
207             }
208         } else {
209             assert targetMethod.isStatic();
210             if (argsIncludingReceiver.length == 0) {
211                 return apply(b, targetMethod, null);
212             } else if (argsIncludingReceiver.length == 1) {
213                 return apply(b, targetMethod, null, argsIncludingReceiver[0]);
214             } else if (argsIncludingReceiver.length == 2) {
215                 return apply(b, targetMethod, null, argsIncludingReceiver[0], argsIncludingReceiver[1]);
216             } else if (argsIncludingReceiver.length == 3) {
217                 return apply(b, targetMethod, null, argsIncludingReceiver[0], argsIncludingReceiver[1], argsIncludingReceiver[2]);
218             } else if (argsIncludingReceiver.length == 4) {
219                 return apply(b, targetMethod, null, argsIncludingReceiver[0], argsIncludingReceiver[1], argsIncludingReceiver[2], argsIncludingReceiver[3]);
220             } else if (argsIncludingReceiver.length == 5) {
221                 return apply(b, targetMethod, null, argsIncludingReceiver[0], argsIncludingReceiver[1], argsIncludingReceiver[2], argsIncludingReceiver[3], argsIncludingReceiver[4]);
222             } else if (argsIncludingReceiver.length == 6) {
223                 return apply(b, targetMethod, null, argsIncludingReceiver[0], argsIncludingReceiver[1], argsIncludingReceiver[2], argsIncludingReceiver[3], argsIncludingReceiver[4],
224                                 argsIncludingReceiver[5]);
225             } else if (argsIncludingReceiver.length == 7) {
226                 return apply(b, targetMethod, null, argsIncludingReceiver[0], argsIncludingReceiver[1], argsIncludingReceiver[2], argsIncludingReceiver[3], argsIncludingReceiver[4],
227                                 argsIncludingReceiver[5], argsIncludingReceiver[6]);
228             } else {
229                 return defaultHandler(b, targetMethod, receiver, argsIncludingReceiver);
230             }
231 
232         }
233     }
234 
235     /**
236      * Handles an invocation when a specific {@code apply} method is not available.
237      */
defaultHandler(@uppressWarningsR) GraphBuilderContext b, ResolvedJavaMethod targetMethod, @SuppressWarnings(R) InvocationPlugin.Receiver receiver, ValueNode... args)238     default boolean defaultHandler(@SuppressWarnings("unused") GraphBuilderContext b, ResolvedJavaMethod targetMethod, @SuppressWarnings("unused") InvocationPlugin.Receiver receiver,
239                     ValueNode... args) {
240         throw new GraalError("Invocation plugin for %s does not handle invocations with %d arguments", targetMethod.format("%H.%n(%p)"), args.length);
241     }
242 
getApplySourceLocation(MetaAccessProvider metaAccess)243     default StackTraceElement getApplySourceLocation(MetaAccessProvider metaAccess) {
244         Class<?> c = getClass();
245         for (Method m : c.getDeclaredMethods()) {
246             if (m.getName().equals("apply")) {
247                 return metaAccess.lookupJavaMethod(m).asStackTraceElement(0);
248             } else if (m.getName().equals("defaultHandler")) {
249                 return metaAccess.lookupJavaMethod(m).asStackTraceElement(0);
250             }
251         }
252         throw new GraalError("could not find method named \"apply\" or \"defaultHandler\" in " + c.getName());
253     }
254 }
255