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 }