1 /*
2 * Copyright (c) 2026, 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.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24 package jdk.incubator.code.runtime;
25
26 import java.lang.invoke.CallSite;
27 import java.lang.invoke.ConstantCallSite;
28 import java.lang.invoke.LambdaConversionException;
29 import java.lang.invoke.MethodHandle;
30 import java.lang.invoke.MethodHandles;
31 import java.lang.invoke.MethodHandles.Lookup;
32 import java.lang.invoke.MethodType;
33 import java.lang.reflect.Member;
34 import java.lang.reflect.Method;
35 import java.util.List;
36 import java.util.Objects;
37 import java.util.stream.Stream;
38 import jdk.incubator.code.Op;
39 import jdk.incubator.code.bytecode.BytecodeGenerator;
40 import jdk.incubator.code.dialect.core.CoreOp;
41 import jdk.incubator.code.dialect.core.CoreType;
42 import jdk.incubator.code.dialect.java.JavaOp;
43
44 /**
45 * Bootstrap methods for linking {@code invokedynamic} call sites that execute
46 * method code models or create lambda instances implemented by code models.
47 *
48 * @see ReflectableLambdaMetafactory
49 */
50 public final class CodeModelBootstraps {
51
52 private CodeModelBootstraps() {
53 }
54
55 /**
56 * Bootstrap method for linking an {@code invokedynamic} call site that
57 * implements execution of a method's code model.
58 * <p>
59 * The method's code model is obtained for the method referenced by the
60 * given method handle. If the method does not have a code model then an
61 * {@code IllegalArgumentException} is thrown.
62 * <p>
63 * Execution of the code model is implemented by transforming the code model
64 * to bytecode and linking it as the target method handle of the returned
65 * {@code CallSite}.
66 * <p>
67 * If model retrieval or transformation fails, the resulting exception or
68 * error is propagated.
69 *
70 * @param lookup Represents a lookup context with the accessibility
71 * privileges of the caller. Specifically, the lookup
72 * context must have
73 * {@linkplain MethodHandles.Lookup#hasFullPrivilegeAccess()
74 * full privilege access}.
75 * When used with {@code invokedynamic}, this is stacked
76 * automatically by the VM.
77 * @param name The name of the method to implement. This name is
78 * arbitrary, and has no meaning for this linkage method.
79 * When used with {@code invokedynamic}, this is provided by
80 * the {@code NameAndType} of the {@code InvokeDynamic}
81 * structure and is stacked automatically by the VM.
82 * @param methodType The expected signature of the {@code CallSite}. When
83 * used with {@code invokedynamic}, this is provided by the
84 * {@code NameAndType} of the {@code InvokeDynamic}
85 * structure and is stacked automatically by the VM.
86 * @param method a direct method handle referencing the original method
87 * @return a constant call site whose target implements the behavior
88 * represented by the stored code model
89 * @throws NullPointerException If any of the incoming arguments is null.
90 * This will never happen when a bootstrap method
91 * is called with {@code invokedynamic}.
92 * @throws IllegalArgumentException if the handle cannot be revealed by
93 * {@code lookup}, does not reference an accessible method, the
94 * method has no code model, or the generated target type differs
95 * from {@code methodType}
96 */
97 public static CallSite codeModelExecutor(MethodHandles.Lookup lookup,
98 String name,
99 MethodType methodType,
100 MethodHandle method) {
101 Objects.requireNonNull(name);
102 Objects.requireNonNull(methodType);
103 Member member = lookup.revealDirect(method).reflectAs(Member.class, lookup);
104 if (!(member instanceof Method m)) {
105 throw new IllegalArgumentException("Handle does not reference a method");
106 }
107 CoreOp.FuncOp model = Op.ofMethod(m).orElseThrow(() ->
108 new IllegalArgumentException("Method has no code model: " + m));
109 MethodHandle target = BytecodeGenerator.generate(lookup, model);
110 if (!target.type().equals(methodType)) {
111 throw new IllegalArgumentException("Code model target type " + target.type()
112 + " differs from call-site type " + methodType);
113 }
114 return new ConstantCallSite(target);
115 }
116
117 /**
118 * Bootstrap method for linking an {@code invokedynamic} call site whose
119 * target creates reflectable lambda instances, using the lambda's stored
120 * code model as its implementation.
121 * <p>
122 * Except for the implementation method handle, this method follows the
123 * contract and encoded-name convention of
124 * {@link ReflectableLambdaMetafactory#metafactory(Lookup, String,
125 * MethodType, MethodType, MethodHandle, MethodType)}.
126 * <p>
127 * The supplied implementation method handle is ignored and replaced
128 * with one linked from the stored code model.
129 *
130 * @param caller the lookup
131 * @param interfaceMethodName the encoded interface-method and code-model
132 * accessor names, as specified by
133 * {@link ReflectableLambdaMetafactory#metafactory(Lookup,
134 * String, MethodType, MethodType, MethodHandle,
135 * MethodType)}
136 * @param factoryType The expected signature of the {@code CallSite}.
137 * @param interfaceMethodType Signature and return type of method to be
138 * implemented by the function object.
139 * @param implementation ignored, retained for compatibility with the
140 standard lambda metafactory bootstrap signature
141 * @param dynamicMethodType The signature and return type that should
142 * be enforced dynamically at invocation time.
143 * @return a call site whose target creates reflectable lambda instances of
144 * the functional interface specified by the return type of
145 * {@code factoryType}; each instance can be inspected using
146 * {@link Op#ofLambda(Object)}
147 *
148 * @throws LambdaConversionException if the implementation cannot be linked
149 * from its stored code model, or if, after the lambda name is
150 * decoded, the parameters of the call are invalid for
151 * {@link ReflectableLambdaMetafactory#metafactory(Lookup, String,
152 * MethodType, MethodType, MethodHandle, MethodType)}
153 * @throws NullPointerException if {@code interfaceMethodName},
154 * {@code factoryType}, {@code interfaceMethodType}, or
155 * {@code dynamicMethodType} is {@code null}
156 *
157 * @see ReflectableLambdaMetafactory#metafactory(Lookup, String, MethodType,
158 * MethodType, MethodHandle, MethodType)
159 * @see Op#ofLambda(Object)
160 */
161 public static CallSite lambdaMetafactory(MethodHandles.Lookup caller,
162 String interfaceMethodName,
163 MethodType factoryType,
164 MethodType interfaceMethodType,
165 MethodHandle implementation,
166 MethodType dynamicMethodType) throws LambdaConversionException {
167 MethodHandle generatedImpl = linkLambdaImplementation(caller, interfaceMethodName);
168 CallSite site = ReflectableLambdaMetafactory.metafactory(caller,
169 interfaceMethodName,
170 generatedFactoryType(factoryType, generatedImpl),
171 interfaceMethodType,
172 generatedImpl,
173 dynamicMethodType);
174 return new ConstantCallSite(site.getTarget().asType(factoryType));
175 }
176
177 /**
178 * Bootstrap method for linking an {@code invokedynamic} call site whose
179 * target creates reflectable lambda instances, using the lambda's stored
180 * code model as its implementation.
181 * <p>
182 * Except for the implementation method handle, this method follows the
183 * contract and encoded-name convention of
184 * {@link ReflectableLambdaMetafactory#altMetafactory(Lookup, String,
185 * MethodType, Object...)}.
186 * <p>
187 * The implementation method handle in {@code args} is replaced with one
188 * linked from the stored code model.
189 *
190 * @param caller the lookup
191 * @param interfaceMethodName the encoded interface-method and code-model
192 * accessor names, as specified by
193 * {@link ReflectableLambdaMetafactory#altMetafactory(Lookup,
194 * String, MethodType, Object...)}
195 * @param factoryType The expected signature of the {@code CallSite}.
196 * @param args An array of {@code Object} containing the required
197 * arguments {@code interfaceMethodType}, {@code implementation},
198 * {@code dynamicMethodType}, {@code flags}, and any
199 * optional arguments, as required by
200 * {@link ReflectableLambdaMetafactory#altMetafactory(Lookup,
201 * String, MethodType, Object...)}, the
202 * {@code implementation} component is ignored and replaced
203 * with one linked from the stored code model
204 * @return a CallSite whose target can be used to perform capture, generating
205 * a reflectable lambda instance implementing the functional
206 * interface specified by the return type of {@code factoryType}.
207 * The code model for such instance can be inspected using
208 * {@link Op#ofLambda(Object)}.
209 *
210 * @throws LambdaConversionException if the implementation cannot be linked
211 * from its stored code model, or if, after the lambda name is
212 * decoded, the parameters of the call are invalid for
213 * {@link ReflectableLambdaMetafactory#altMetafactory(Lookup, String,
214 * MethodType, Object...)}
215 * @throws NullPointerException if {@code interfaceMethodName},
216 * {@code factoryType}, or {@code args} is {@code null}, or if a
217 * required component of {@code args} other than
218 * {@code implementation} is {@code null}
219 * @throws IllegalArgumentException If {@code args} are invalid for
220 * {@link ReflectableLambdaMetafactory#altMetafactory(Lookup, String,
221 * MethodType, Object...)}
222 *
223 * @see ReflectableLambdaMetafactory#altMetafactory(Lookup, String, MethodType,
224 * Object...)
225 * @see Op#ofLambda(Object)
226 */
227 public static CallSite lambdaAltMetafactory(MethodHandles.Lookup caller,
228 String interfaceMethodName,
229 MethodType factoryType,
230 Object... args) throws LambdaConversionException {
231 MethodHandle generatedImpl = linkLambdaImplementation(caller, interfaceMethodName);
232 args[1] = generatedImpl;
233 CallSite site = ReflectableLambdaMetafactory.altMetafactory(caller,
234 interfaceMethodName,
235 generatedFactoryType(factoryType, generatedImpl),
236 args);
237 return new ConstantCallSite(site.getTarget().asType(factoryType));
238 }
239
240 private static MethodType generatedFactoryType(MethodType factoryType, MethodHandle implementation) {
241 MethodType implementationType = implementation.type();
242 return implementationType.dropParameterTypes(factoryType.parameterCount(), implementationType.parameterCount())
243 .changeReturnType(factoryType.returnType());
244 }
245
246 private static MethodHandle linkLambdaImplementation(MethodHandles.Lookup caller,
247 String interfaceMethodName)
248 throws LambdaConversionException {
249 String modelMethodName = interfaceMethodName.split("=")[1];
250 try {
251 MethodHandle opHandle = caller.findStatic(caller.lookupClass(),
252 modelMethodName,
253 MethodType.methodType(Op.class));
254 MethodHandle methodHandle = BytecodeGenerator.generate(caller,
255 unquoteLambda((CoreOp.FuncOp)opHandle.invoke()));
256 return methodHandle;
257 } catch (Throwable t) {
258 throw new LambdaConversionException(t);
259 }
260 }
261
262 // flatten the quoted lambda into the enclosing function model
263 private static CoreOp.FuncOp unquoteLambda(CoreOp.FuncOp funcOp) {
264 int capturedValues = funcOp.parameters().size();
265 List<Op> ops = funcOp.body().entryBlock().ops();
266 JavaOp.LambdaOp lambda = (JavaOp.LambdaOp)((CoreOp.QuotedOp)ops.get(ops.size() - 2)).quotedOp();
267 return CoreOp.func(funcOp.funcName(), CoreType.functionType(
268 lambda.body().yieldType(),
269 Stream.of(funcOp.invokableSignature().parameterTypes(),
270 lambda.invokableSignature().parameterTypes()).flatMap(List::stream).toList())).body(bb -> {
271 bb.context().mapValues(funcOp.parameters(), bb.parameters().subList(0, capturedValues));
272 for (int i = 0; i < ops.size() - 2; i++) {
273 Op o = ops.get(i);
274 bb.add(o);
275 }
276 bb.transformBody(lambda.body(),
277 bb.parameters().subList(capturedValues, bb.parameters().size()));
278 });
279 }
280 }