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 }