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 }