1 /*
2 * Copyright (c) 2024, 2025, 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package jdk.incubator.code.dialect.java;
27
28 import java.lang.constant.ClassDesc;
29 import java.lang.constant.MethodTypeDesc;
30
31 import jdk.incubator.code.dialect.core.CoreType;
32 import jdk.incubator.code.dialect.java.JavaOp.InvokeOp.InvokeKind;
33 import jdk.incubator.code.dialect.java.impl.MethodRefImpl;
34 import java.lang.invoke.MethodHandle;
35 import java.lang.invoke.MethodHandles;
36 import java.lang.invoke.MethodHandles.Lookup;
37 import java.lang.invoke.MethodType;
38 import java.lang.reflect.Constructor;
39 import java.lang.reflect.Method;
40 import jdk.incubator.code.TypeElement;
41 import jdk.incubator.code.dialect.core.FunctionType;
42
43 import java.util.List;
44
45 import static jdk.incubator.code.dialect.core.CoreType.functionType;
46
47 /**
48 * The symbolic reference to a Java method, called the <em>target method</em>.
49 * <p>
50 * All method references are defined in terms of the following attributes:
51 * <ul>
52 * <li>an <em>owner type</em>, the type of which the target method is a member;</li>
53 * <li>a <em>name</em>, the name of the target method.</li>
54 * <li>a <em>type</em>, the type of the target method.</li>
55 * </ul>
56 * Some method references, called <em>constructor references</em> are used to model a Java constructor, called
57 * the <em>target constructor</em>. The name of a constructor reference is always the special name {@code "<init>"}.
58 * <p>
59 * Method references can be <em>resolved</em> to their corresponding {@linkplain #resolveToMethod(Lookup) target method} or
60 * {@linkplain #resolveToMethod(Lookup) target constructor}. Or they can be turned into a
61 * {@linkplain #resolveToHandle(Lookup, InvokeKind) method handle} that can be used to invoke the target method or constructor.
62 */
63 public sealed interface MethodRef extends JavaRef, TypeVariableType.Owner
64 permits MethodRefImpl {
65
66 /**
67 * {@return the owner type of this method reference}
68 */
69 TypeElement refType();
70
71 /**
72 * {@return the name of this method reference}
73 */
74 String name();
75
76 /**
77 * {@return the type of this method reference}
78 */
79 FunctionType type();
80
81 /**
82 * {@return {@code true}, if this method reference is a constructor reference}
83 */
84 boolean isConstructor();
85
86 // Resolutions to methods, constructors and method handles
87
88 /**
89 * Resolves the target method associated with this method reference.
90 * @return the method associated with this method reference
91 * @param l the lookup used for resolving this method reference
92 * @throws ReflectiveOperationException if a resolution error occurs
93 * @throws UnsupportedOperationException if this reference is a constructor reference
94 */
95 Method resolveToMethod(MethodHandles.Lookup l) throws ReflectiveOperationException;
96
97 /**
98 * Resolves the target method associated with this method reference.
99 * @return the constructor associated with this constructor reference
100 * @param l the lookup used for resolving this method reference
101 * @throws ReflectiveOperationException if a resolution error occurs
102 * @throws UnsupportedOperationException if this reference is not a constructor reference
103 * @throws IllegalArgumentException if the provided {@code kind} is unsupported for this method reference
104 */
105 Constructor<?> resolveToConstructor(MethodHandles.Lookup l) throws ReflectiveOperationException;
106
107 /**
108 * {@return a method handle used to invoke the target method or constructor associated with this method reference}
109 * The method handle is obtained by invoking the corresponding method on the provided lookup, as determined by
110 * the provided {@code kind}:
111 * <ul>
112 * <li>if <code>kind == SUPER && isConstructor()</code>, then {@link MethodHandles.Lookup#findConstructor(Class, MethodType)} is used;</li>
113 * <li>if <code>kind == STATIC && !isConstructor()</code>, then {@link MethodHandles.Lookup#findStatic(Class, String, MethodType)} is used;</li>
114 * <li>if <code>kind == INSTANCE && !isConstructor()</code>, then {@link MethodHandles.Lookup#findVirtual(Class, String, MethodType)} is used;</li>
115 * <li>if <code>kind == SUPER && !isConstructor()</code>, then {@link MethodHandles.Lookup#findSpecial(Class, String, MethodType, Class)} is used;</li>
116 * <li>otherwise, the provided {@code kind} is unsupported for this method reference, and {@link IllegalArgumentException} is thrown</li>.
117 * </ul>
118 * @param l the lookup used for resolving this method reference
119 * @param kind the invocation kind to be used for resolving this method reference
120 * @throws ReflectiveOperationException if a resolution error occurs
121 * @throws IllegalArgumentException if the provided {@code kind} is unsupported for this method reference
122 */
123 MethodHandle resolveToHandle(MethodHandles.Lookup l, JavaOp.InvokeOp.InvokeKind kind) throws ReflectiveOperationException;
124
125 // Method factories
126
127 /**
128 * {@return a method reference obtained from the provided method}
129 * @param m a reflective method
130 */
131 static MethodRef method(Method m) {
132 return method(m.getDeclaringClass(), m.getName(),
133 m.getReturnType(),
134 m.getParameterTypes());
135 }
136
137 /**
138 * {@return a method reference obtained from the provided owner, name and type}
139 * @param refType the reference owner type
140 * @param name the reference name
141 * @param mt the reference type
142 */
143 static MethodRef method(Class<?> refType, String name, MethodType mt) {
144 return method(refType, name, mt.returnType(), mt.parameterList());
145 }
146
147 /**
148 * {@return a method reference obtained from the provided owner, name, return type and parameter types}
149 * @param refType the reference owner type
150 * @param name the reference name
151 * @param retType the reference return type
152 * @param params the reference parameter types
153 */
154 static MethodRef method(Class<?> refType, String name, Class<?> retType, Class<?>... params) {
155 return method(refType, name, retType, List.of(params));
156 }
157
158 /**
159 * {@return a method reference obtained from the provided owner, name, return type and parameter types}
160 * @param refType the reference owner type
161 * @param name the reference name
162 * @param retType the reference return type
163 * @param params the reference parameter types
164 */
165 static MethodRef method(Class<?> refType, String name, Class<?> retType, List<Class<?>> params) {
166 return method(JavaType.type(refType), name, JavaType.type(retType), params.stream().map(JavaType::type).toList());
167 }
168
169 /**
170 * {@return a method reference obtained from the provided owner, name and type}
171 * @param refType the reference owner type
172 * @param name the reference name
173 * @param type the reference type
174 */
175 static MethodRef method(TypeElement refType, String name, FunctionType type) {
176 return new MethodRefImpl(refType, name, type);
177 }
178
179 /**
180 * {@return a method reference obtained from the provided owner, name, return type and parameter types}
181 * @param refType the reference owner type
182 * @param name the reference name
183 * @param retType the reference return type
184 * @param params the reference parameter types
185 */
186 static MethodRef method(TypeElement refType, String name, TypeElement retType, TypeElement... params) {
187 return method(refType, name, functionType(retType, params));
188 }
189
190 /**
191 * {@return a method reference obtained from the provided owner, name, return type and parameter types}
192 * @param refType the reference owner type
193 * @param name the reference name
194 * @param retType the reference return type
195 * @param params the reference parameter types
196 */
197 static MethodRef method(TypeElement refType, String name, TypeElement retType, List<? extends TypeElement> params) {
198 return method(refType, name, functionType(retType, params));
199 }
200
201 // Constructor factories
202
203 /**
204 * {@return a method reference obtained from the provided constructor}
205 * @param c a reflective constructor
206 */
207 static MethodRef constructor(Constructor<?> c) {
208 return constructor(c.getDeclaringClass(),
209 c.getParameterTypes());
210 }
211
212 /**
213 * {@return a method reference obtained from the provided type}
214 * The owner type of the returned method reference is the return type of the provided type.
215 * @param mt the reference type
216 */
217 static MethodRef constructor(MethodType mt) {
218 return constructor(mt.returnType(), mt.parameterList());
219 }
220
221 /**
222 * {@return a method reference obtained from the provided owner and parameter types}
223 * @param refType the reference owner type
224 * @param params the reference parameter types
225 */
226 static MethodRef constructor(Class<?> refType, Class<?>... params) {
227 return constructor(refType, List.of(params));
228 }
229
230 /**
231 * {@return a method reference obtained from the provided owner and parameter types}
232 * @param refType the reference owner type
233 * @param params the reference parameter types
234 */
235 static MethodRef constructor(Class<?> refType, List<Class<?>> params) {
236 return constructor(JavaType.type(refType), params.stream().map(JavaType::type).toList());
237 }
238
239 /**
240 * {@return a method reference obtained from the provided owner and parameter types}
241 * @param refType the reference owner type
242 * @param params the reference parameter types
243 */
244 static MethodRef constructor(TypeElement refType, List<? extends TypeElement> params) {
245 return constructor(functionType(refType, params));
246 }
247
248 /**
249 * {@return a method reference obtained from the provided owner and parameter types}
250 * @param refType the reference owner type
251 * @param params the reference parameter types
252 */
253 static MethodRef constructor(TypeElement refType, TypeElement... params) {
254 return constructor(functionType(refType, params));
255 }
256
257 /**
258 * {@return a method reference obtained from the provided type}
259 * The owner type of the returned method reference is the return type of the provided type.
260 * @param type the reference type
261 */
262 static MethodRef constructor(FunctionType type) {
263 return new MethodRefImpl(type.returnType(), INIT_NAME, type);
264 }
265
266 // MethodTypeDesc factories
267 // @@@ Where else to place them?
268
269 static FunctionType ofNominalDescriptor(MethodTypeDesc d) {
270 return CoreType.functionType(
271 JavaType.type(d.returnType()),
272 d.parameterList().stream().map(JavaType::type).toList());
273 }
274
275 static MethodTypeDesc toNominalDescriptor(FunctionType t) {
276 return MethodTypeDesc.of(
277 toClassDesc(t.returnType()),
278 t.parameterTypes().stream().map(MethodRef::toClassDesc).toList());
279 }
280
281 private static ClassDesc toClassDesc(TypeElement e) {
282 if (!(e instanceof JavaType jt)) {
283 throw new IllegalArgumentException();
284 }
285
286 return jt.toNominalDescriptor();
287 }
288
289 /**
290 * The name of a constructor reference
291 */
292 String INIT_NAME = "<init>";
293 }