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.constant.MethodHandleDesc;
12 import java.lang.invoke.CallSite;
13 import java.lang.invoke.LambdaConversionException;
14 import java.lang.invoke.LambdaMetafactory;
15 import java.lang.invoke.MethodHandle;
16 import java.lang.invoke.MethodHandles;
17 import java.lang.invoke.MethodType;
18 import java.lang.invoke.SerializedLambda;
19
20 public class ReflectableLambdaMetafactory {
21
22 private ReflectableLambdaMetafactory() {
23 // nope
24 }
25
26 /**
27 * Facilitates the creation of simple "function objects" that implement one
28 * or more interfaces by delegation to a provided {@link MethodHandle},
29 * after appropriate type adaptation and partial evaluation of arguments.
30 * Typically used as a <em>bootstrap method</em> for {@code invokedynamic}
31 * call sites, to support the <em>lambda expression</em> and <em>method
32 * reference expression</em> features of the Java Programming Language.
33 *
34 * <p>This is the standard, streamlined metafactory; additional flexibility
35 * is provided by {@link #altMetafactory(MethodHandles.Lookup, String, MethodType, Object...)}.
36 * A general description of the behavior of this method is provided
37 * {@link LambdaMetafactory above}.
38 *
39 * <p>When the target of the {@code CallSite} returned from this method is
40 * invoked, the resulting function objects are instances of a class which
41 * implements the interface named by the return type of {@code factoryType},
42 * declares a method with the name given by {@code interfaceMethodName} and the
43 * signature given by {@code interfaceMethodType}. It may also override additional
44 * methods from {@code Object}.
45 *
46 * @param caller Represents a lookup context with the accessibility
47 * privileges of the caller. Specifically, the lookup context
48 * must have {@linkplain MethodHandles.Lookup#hasFullPrivilegeAccess()
49 * full privilege access}.
50 * When used with {@code invokedynamic}, this is stacked
51 * automatically by the VM.
52 * @param interfaceMethodName The name of the method to implement. When used with
53 * {@code invokedynamic}, this is provided by the
54 * {@code NameAndType} of the {@code InvokeDynamic}
55 * structure and is stacked automatically by the VM.
56 * @param factoryType The expected signature of the {@code CallSite}. The
57 * parameter types represent the types of capture variables;
58 * the return type is the interface to implement. When
59 * used with {@code invokedynamic}, this is provided by
60 * the {@code NameAndType} of the {@code InvokeDynamic}
61 * structure and is stacked automatically by the VM.
62 * @param interfaceMethodType Signature and return type of method to be
63 * implemented by the function object.
64 * @param implementation A direct method handle describing the implementation
65 * method which should be called (with suitable adaptation
66 * of argument types and return types, and with captured
67 * arguments prepended to the invocation arguments) at
68 * invocation time.
69 * @param dynamicMethodType The signature and return type that should
70 * be enforced dynamically at invocation time.
71 * In simple use cases this is the same as
72 * {@code interfaceMethodType}.
73 * @return a CallSite whose target can be used to perform capture, generating
74 * instances of the interface named by {@code factoryType}
75 * @throws LambdaConversionException If {@code caller} does not have full privilege
76 * access, or if {@code interfaceMethodName} is not a valid JVM
77 * method name, or if the return type of {@code factoryType} is not
78 * an interface, or if {@code implementation} is not a direct method
79 * handle referencing a method or constructor, or if the linkage
80 * invariants are violated, as defined {@link LambdaMetafactory above}.
81 * @throws NullPointerException If any argument is {@code null}.
82 */
83 public static CallSite metafactory(MethodHandles.Lookup caller,
84 String interfaceMethodName,
85 MethodType factoryType,
86 MethodType interfaceMethodType,
87 MethodHandle implementation,
88 MethodType dynamicMethodType)
89 throws LambdaConversionException {
90 DecodedName decodedName = findQuotableOpGetter(caller, interfaceMethodName);
91 return JLI_ACCESS.metafactoryInternal(caller, decodedName.name, factoryType, interfaceMethodType,
92 implementation, dynamicMethodType, decodedName.reflectableLambdaInfo);
93 }
94
95 /**
96 * Facilitates the creation of simple "function objects" that implement one
97 * or more interfaces by delegation to a provided {@link MethodHandle},
98 * after appropriate type adaptation and partial evaluation of arguments.
99 * Typically used as a <em>bootstrap method</em> for {@code invokedynamic}
100 * call sites, to support the <em>lambda expression</em> and <em>method
101 * reference expression</em> features of the Java Programming Language.
102 *
103 * <p>This is the general, more flexible metafactory; a streamlined version
104 * is provided by {@link #metafactory(java.lang.invoke.MethodHandles.Lookup,
105 * String, MethodType, MethodType, MethodHandle, MethodType)}.
106 * A general description of the behavior of this method is provided
107 * {@link LambdaMetafactory above}.
108 *
109 * <p>The argument list for this method includes three fixed parameters,
110 * corresponding to the parameters automatically stacked by the VM for the
111 * bootstrap method in an {@code invokedynamic} invocation, and an {@code Object[]}
112 * parameter that contains additional parameters. The declared argument
113 * list for this method is:
114 *
115 * <pre>{@code
116 * CallSite altMetafactory(MethodHandles.Lookup caller,
117 * String interfaceMethodName,
118 * MethodType factoryType,
119 * Object... args)
120 * }</pre>
121 *
122 * <p>but it behaves as if the argument list is as follows:
123 *
124 * <pre>{@code
125 * CallSite altMetafactory(MethodHandles.Lookup caller,
126 * String interfaceMethodName,
127 * MethodType factoryType,
128 * MethodType interfaceMethodType,
129 * MethodHandle implementation,
130 * MethodType dynamicMethodType,
131 * int flags,
132 * int altInterfaceCount, // IF flags has MARKERS set
133 * Class... altInterfaces, // IF flags has MARKERS set
134 * int altMethodCount, // IF flags has BRIDGES set
135 * MethodType... altMethods // IF flags has BRIDGES set
136 * )
137 * }</pre>
138 *
139 * <p>Arguments that appear in the argument list for
140 * {@link #metafactory(MethodHandles.Lookup, String, MethodType, MethodType, MethodHandle, MethodType)}
141 * have the same specification as in that method. The additional arguments
142 * are interpreted as follows:
143 * <ul>
144 * <li>{@code flags} indicates additional options; this is a bitwise
145 * OR of desired flags. Defined flags are {@link LambdaMetafactory#FLAG_BRIDGES},
146 * {@link LambdaMetafactory#FLAG_MARKERS}, and {@link LambdaMetafactory#FLAG_SERIALIZABLE}.</li>
147 * <li>{@code altInterfaceCount} is the number of additional interfaces
148 * the function object should implement, and is present if and only if the
149 * {@code FLAG_MARKERS} flag is set.</li>
150 * <li>{@code altInterfaces} is a variable-length list of additional
151 * interfaces to implement, whose length equals {@code altInterfaceCount},
152 * and is present if and only if the {@code FLAG_MARKERS} flag is set.</li>
153 * <li>{@code altMethodCount} is the number of additional method signatures
154 * the function object should implement, and is present if and only if
155 * the {@code FLAG_BRIDGES} flag is set.</li>
156 * <li>{@code altMethods} is a variable-length list of additional
157 * methods signatures to implement, whose length equals {@code altMethodCount},
158 * and is present if and only if the {@code FLAG_BRIDGES} flag is set.</li>
159 * </ul>
160 *
161 * <p>Each class named by {@code altInterfaces} is subject to the same
162 * restrictions as {@code Rd}, the return type of {@code factoryType},
163 * as described {@link LambdaMetafactory above}. Each {@code MethodType}
164 * named by {@code altMethods} is subject to the same restrictions as
165 * {@code interfaceMethodType}, as described {@link LambdaMetafactory above}.
166 *
167 * <p>When FLAG_SERIALIZABLE is set in {@code flags}, the function objects
168 * will implement {@code Serializable}, and will have a {@code writeReplace}
169 * method that returns an appropriate {@link SerializedLambda}. The
170 * {@code caller} class must have an appropriate {@code $deserializeLambda$}
171 * method, as described in {@link SerializedLambda}.
172 *
173 * <p>When the target of the {@code CallSite} returned from this method is
174 * invoked, the resulting function objects are instances of a class with
175 * the following properties:
176 * <ul>
177 * <li>The class implements the interface named by the return type
178 * of {@code factoryType} and any interfaces named by {@code altInterfaces}</li>
179 * <li>The class declares methods with the name given by {@code interfaceMethodName},
180 * and the signature given by {@code interfaceMethodType} and additional signatures
181 * given by {@code altMethods}</li>
182 * <li>The class may override methods from {@code Object}, and may
183 * implement methods related to serialization.</li>
184 * </ul>
185 *
186 * @param caller Represents a lookup context with the accessibility
187 * privileges of the caller. Specifically, the lookup context
188 * must have {@linkplain MethodHandles.Lookup#hasFullPrivilegeAccess()
189 * full privilege access}.
190 * When used with {@code invokedynamic}, this is stacked
191 * automatically by the VM.
192 * @param interfaceMethodName The name of the method to implement. When used with
193 * {@code invokedynamic}, this is provided by the
194 * {@code NameAndType} of the {@code InvokeDynamic}
195 * structure and is stacked automatically by the VM.
196 * @param factoryType The expected signature of the {@code CallSite}. The
197 * parameter types represent the types of capture variables;
198 * the return type is the interface to implement. When
199 * used with {@code invokedynamic}, this is provided by
200 * the {@code NameAndType} of the {@code InvokeDynamic}
201 * structure and is stacked automatically by the VM.
202 * @param args An array of {@code Object} containing the required
203 * arguments {@code interfaceMethodType}, {@code implementation},
204 * {@code dynamicMethodType}, {@code flags}, and any
205 * optional arguments, as described above
206 * @return a CallSite whose target can be used to perform capture, generating
207 * instances of the interface named by {@code factoryType}
208 * @throws LambdaConversionException If {@code caller} does not have full privilege
209 * access, or if {@code interfaceMethodName} is not a valid JVM
210 * method name, or if the return type of {@code factoryType} is not
211 * an interface, or if any of {@code altInterfaces} is not an
212 * interface, or if {@code implementation} is not a direct method
213 * handle referencing a method or constructor, or if the linkage
214 * invariants are violated, as defined {@link LambdaMetafactory above}.
215 * @throws NullPointerException If any argument, or any component of {@code args},
216 * is {@code null}.
217 * @throws IllegalArgumentException If the number or types of the components
218 * of {@code args} do not follow the above rules, or if
219 * {@code altInterfaceCount} or {@code altMethodCount} are negative
220 * integers.
221 */
222 public static CallSite altMetafactory(MethodHandles.Lookup caller,
223 String interfaceMethodName,
224 MethodType factoryType,
225 Object... args)
226 throws LambdaConversionException {
227 DecodedName decodedName = findQuotableOpGetter(caller, interfaceMethodName);
228 return JLI_ACCESS.altMetafactoryInternal(caller, decodedName.name, factoryType, decodedName.reflectableLambdaInfo, args);
229 }
230
231 static final JavaLangInvokeAccess JLI_ACCESS = SharedSecrets.getJavaLangInvokeAccess();
232
233 record DecodedName(String name, ReflectableLambdaInfo reflectableLambdaInfo) { }
234
235 private static DecodedName findQuotableOpGetter(MethodHandles.Lookup lookup, String interfaceMethodName) throws LambdaConversionException {
236 String[] implNameParts = interfaceMethodName.split("=");
237 if (implNameParts.length != 2) {
238 throw new LambdaConversionException("Bad method name: " + interfaceMethodName);
239 }
240 try {
241 return new DecodedName(
242 implNameParts[0],
243 newReflectableLambdaInfo(lookup.findStatic(lookup.lookupClass(), implNameParts[1], MethodType.methodType(Op.class))));
244 } catch (ReflectiveOperationException ex) {
245 throw new LambdaConversionException(ex);
246 }
247 }
248
249 private static ReflectableLambdaInfo newReflectableLambdaInfo(MethodHandle handle) {
250 class Holder {
251 static final ClassDesc QUOTED_CLASS_DESC = Quoted.class.describeConstable().get();
252 static final ClassDesc FUNC_OP_CLASS_DESC = FuncOp.class.describeConstable().get();
253 static final MethodHandle QUOTED_EXTRACT_OP_HANDLE;
254
255 static {
256 try {
257 QUOTED_EXTRACT_OP_HANDLE = MethodHandles.lookup()
258 .findStatic(Quoted.class, "extractOp",
259 MethodType.methodType(Quoted.class, FuncOp.class, Object[].class));
260 } catch (Throwable ex) {
261 throw new ExceptionInInitializerError(ex);
262 }
263 }
264 }
265 return new ReflectableLambdaInfo(Holder.QUOTED_CLASS_DESC, Holder.FUNC_OP_CLASS_DESC,
266 Holder.QUOTED_EXTRACT_OP_HANDLE, handle);
267 }
268 }