1 package jdk.incubator.code.runtime;
  2 
  3 import jdk.incubator.code.Op;
  4 import jdk.incubator.code.Quoted;
  5 import jdk.incubator.code.dialect.core.CoreOp.FuncOp;
  6 import jdk.internal.access.JavaLangInvokeAccess;
  7 import jdk.internal.access.JavaLangInvokeAccess.ReflectableLambdaInfo;
  8 import jdk.internal.access.SharedSecrets;
  9 
 10 import java.lang.constant.ClassDesc;
 11 import java.lang.invoke.CallSite;
 12 import java.lang.invoke.LambdaConversionException;
 13 import java.lang.invoke.LambdaMetafactory;
 14 import java.lang.invoke.MethodHandle;
 15 import java.lang.invoke.MethodHandles;
 16 import java.lang.invoke.MethodHandles.Lookup;
 17 import java.lang.invoke.MethodType;
 18 
 19 /**
 20  * Provides runtime support for creating reflectable lambdas. A reflectable lambda is a lambda whose
 21  * code model can be inspected using {@link Op#ofLambda(Object)}.
 22  * @see LambdaMetafactory
 23  * @see Op#ofLambda(Object)
 24  */
 25 public class ReflectableLambdaMetafactory {
 26 
 27     private ReflectableLambdaMetafactory() {
 28         // nope
 29     }
 30 
 31     /**
 32      * Metafactory used to create a reflectable lambda.
 33      * <p>
 34      * The functionality provided by this metafactory is identical to that in
 35      * {@link LambdaMetafactory#metafactory(Lookup, String, MethodType, MethodType, MethodHandle, MethodType)}
 36      * with one important difference: this metafactory expects the provided name to be encoded in the following form:
 37      * <code>
 38      *     lambdaName=opMethodName
 39      * </code>
 40      * The {@code lambdaName} part of the name is passed to the regular metafactory method, along with all the other
 41      * parameters unchanged. The {@code opMethod} part of the name is used to locate a method in the
 42      * {@linkplain Lookup#lookupClass() lookup class} associated with the provided lookup. This method is expected
 43      * to accept no parameters and return a {@link Op}, namely the model of the reflectable lambda.
 44      * <p>
 45      * This means the clients can pass the lambda returned by this factory to the {@link Op#ofLambda(Object)} method,
 46      * to access the code model of the lambda expression dynamically.
 47      *
 48      * @param caller The lookup
 49      * @param interfaceMethodName The name of the method to implement.
 50      *                            This is encoded in the format described above.
 51      * @param factoryType The expected signature of the {@code CallSite}.
 52      * @param interfaceMethodType Signature and return type of method to be
 53      *                            implemented by the function object.
 54      * @param implementation A direct method handle describing the implementation
 55      *                       method which should be called at invocation time.
 56      * @param dynamicMethodType The signature and return type that should
 57      *                          be enforced dynamically at invocation time.
 58      * @return a CallSite whose target can be used to perform capture, generating
 59      *         a reflectable lambda instance implementing the interface named by {@code factoryType}.
 60      *         The code model for such instance can be inspected using {@link Op#ofLambda(Object)}.
 61      *
 62      * @throws LambdaConversionException If, after the lambda name is decoded,
 63      *         the parameters of the call are invalid for
 64      *         {@link LambdaMetafactory#metafactory(Lookup, String, MethodType, MethodType, MethodHandle, MethodType)}
 65      * @throws NullPointerException If any argument is {@code null}.
 66      *
 67      * @see LambdaMetafactory#metafactory(Lookup, String, MethodType, MethodType, MethodHandle, MethodType)
 68      * @see Op#ofLambda(Object)
 69      */
 70     public static CallSite metafactory(MethodHandles.Lookup caller,
 71                                        String interfaceMethodName,
 72                                        MethodType factoryType,
 73                                        MethodType interfaceMethodType,
 74                                        MethodHandle implementation,
 75                                        MethodType dynamicMethodType)
 76             throws LambdaConversionException {
 77         DecodedName decodedName = findReflectableOpGetter(caller, interfaceMethodName);
 78         return JLI_ACCESS.metafactoryInternal(caller, decodedName.name, factoryType, interfaceMethodType,
 79                 implementation, dynamicMethodType, decodedName.reflectableLambdaInfo);
 80     }
 81 
 82     /**
 83      * Metafactory used to create a reflectable lambda.
 84      * <p>
 85      * The functionality provided by this metafactory is identical to that in
 86      * {@link LambdaMetafactory#altMetafactory(Lookup, String, MethodType, Object...)}
 87      * with one important difference: this metafactory expects the provided name to be encoded in the following form:
 88      * <code>
 89      *     lambdaName=opMethodName
 90      * </code>
 91      * The {@code lambdaName} part of the name is passed to the regular metafactory method, along with all the other
 92      * parameters unchanged. The {@code opMethod} part of the name is used to locate a method in the
 93      * {@linkplain Lookup#lookupClass() lookup class} associated with the provided lookup. This method is expected
 94      * to accept no parameters and return a {@link Op}, namely the model of the reflectable lambda.
 95      * <p>
 96      * This means the clients can pass the lambda returned by this factory to the {@link Op#ofLambda(Object)} method,
 97      * to access the code model of the lambda expression dynamically.
 98      *
 99      * @param caller The lookup
100      * @param interfaceMethodName The name of the method to implement.
101      *                            This is encoded in the format described above.
102      * @param factoryType The expected signature of the {@code CallSite}.
103      * @param args An array of {@code Object} containing the required
104      *              arguments {@code interfaceMethodType}, {@code implementation},
105      *              {@code dynamicMethodType}, {@code flags}, and any
106      *              optional arguments, as required by {@link LambdaMetafactory#altMetafactory(Lookup, String, MethodType, Object...)}
107      * @return a CallSite whose target can be used to perform capture, generating
108      *         a reflectable lambda instance implementing the interface named by {@code factoryType}.
109      *         The code model for such instance can be inspected using {@link Op#ofLambda(Object)}.
110      *
111      * @throws LambdaConversionException If, after the lambda name is decoded,
112      *         the parameters of the call are invalid for
113      *         {@link LambdaMetafactory#altMetafactory(Lookup, String, MethodType, Object...)}
114      * @throws NullPointerException If any argument, or any component of {@code args},
115      *         is {@code null}.
116      * @throws IllegalArgumentException If {@code args} are invalid for
117      *         {@link LambdaMetafactory#altMetafactory(Lookup, String, MethodType, Object...)}
118      *
119      * @see LambdaMetafactory#altMetafactory(Lookup, String, MethodType, Object...)
120      * @see Op#ofLambda(Object)
121      */
122     public static CallSite altMetafactory(MethodHandles.Lookup caller,
123                                           String interfaceMethodName,
124                                           MethodType factoryType,
125                                           Object... args)
126             throws LambdaConversionException {
127         DecodedName decodedName = findReflectableOpGetter(caller, interfaceMethodName);
128         return JLI_ACCESS.altMetafactoryInternal(caller, decodedName.name, factoryType, decodedName.reflectableLambdaInfo, args);
129     }
130 
131     static final JavaLangInvokeAccess JLI_ACCESS = SharedSecrets.getJavaLangInvokeAccess();
132 
133     record DecodedName(String name, ReflectableLambdaInfo reflectableLambdaInfo) { }
134 
135     private static DecodedName findReflectableOpGetter(MethodHandles.Lookup lookup, String interfaceMethodName) throws LambdaConversionException {
136         String[] implNameParts = interfaceMethodName.split("=");
137         if (implNameParts.length != 2) {
138             throw new LambdaConversionException("Bad method name: " + interfaceMethodName);
139         }
140         try {
141             return new DecodedName(
142                     implNameParts[0],
143                     newReflectableLambdaInfo(lookup.findStatic(lookup.lookupClass(), implNameParts[1], MethodType.methodType(Op.class))));
144         } catch (ReflectiveOperationException ex) {
145             throw new LambdaConversionException(ex);
146         }
147     }
148 
149     private static ReflectableLambdaInfo newReflectableLambdaInfo(MethodHandle handle) {
150         class Holder {
151             static final ClassDesc QUOTED_CLASS_DESC = Quoted.class.describeConstable().get();
152             static final ClassDesc FUNC_OP_CLASS_DESC = FuncOp.class.describeConstable().get();
153             static final MethodHandle QUOTED_EXTRACT_OP_HANDLE;
154 
155             static {
156                 try {
157                     QUOTED_EXTRACT_OP_HANDLE = MethodHandles.lookup()
158                             .findStatic(Quoted.class, "extractOp",
159                                     MethodType.methodType(Quoted.class, FuncOp.class, Object[].class));
160                 } catch (Throwable ex) {
161                     throw new ExceptionInInitializerError(ex);
162                 }
163             }
164         }
165         return new ReflectableLambdaInfo(Holder.QUOTED_CLASS_DESC, Holder.FUNC_OP_CLASS_DESC,
166                 Holder.QUOTED_EXTRACT_OP_HANDLE, handle);
167     }
168 }