diff a/src/jdk.incubator.code/share/classes/jdk/incubator/code/runtime/CodeModelBootstraps.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/runtime/CodeModelBootstraps.java --- /dev/null +++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/runtime/CodeModelBootstraps.java @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.code.runtime; + +import java.lang.invoke.CallSite; +import java.lang.invoke.ConstantCallSite; +import java.lang.invoke.LambdaConversionException; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.invoke.MethodType; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Objects; +import java.util.stream.Stream; +import jdk.incubator.code.Op; +import jdk.incubator.code.bytecode.BytecodeGenerator; +import jdk.incubator.code.dialect.core.CoreOp; +import jdk.incubator.code.dialect.core.CoreType; +import jdk.incubator.code.dialect.java.JavaOp; + +/** + * Bootstrap methods for linking {@code invokedynamic} call sites that execute + * method code models or create lambda instances implemented by code models. + * + * @see ReflectableLambdaMetafactory + */ +public final class CodeModelBootstraps { + + private CodeModelBootstraps() { + } + + /** + * Bootstrap method for linking an {@code invokedynamic} call site that + * implements execution of a method's code model. + *
+ * The method's code model is obtained for the method referenced by the + * given method handle. If the method does not have a code model then an + * {@code IllegalArgumentException} is thrown. + *
+ * Execution of the code model is implemented by transforming the code model + * to bytecode and linking it as the target method handle of the returned + * {@code CallSite}. + *
+ * If model retrieval or transformation fails, the resulting exception or + * error is propagated. + * + * @param lookup Represents a lookup context with the accessibility + * privileges of the caller. Specifically, the lookup + * context must have + * {@linkplain MethodHandles.Lookup#hasFullPrivilegeAccess() + * full privilege access}. + * When used with {@code invokedynamic}, this is stacked + * automatically by the VM. + * @param name The name of the method to implement. This name is + * arbitrary, and has no meaning for this linkage method. + * When used with {@code invokedynamic}, this is provided by + * the {@code NameAndType} of the {@code InvokeDynamic} + * structure and is stacked automatically by the VM. + * @param methodType The expected signature of the {@code CallSite}. When + * used with {@code invokedynamic}, this is provided by the + * {@code NameAndType} of the {@code InvokeDynamic} + * structure and is stacked automatically by the VM. + * @param method a direct method handle referencing the original method + * @return a constant call site whose target implements the behavior + * represented by the stored code model + * @throws NullPointerException If any of the incoming arguments is null. + * This will never happen when a bootstrap method + * is called with {@code invokedynamic}. + * @throws IllegalArgumentException if the handle cannot be revealed by + * {@code lookup}, does not reference an accessible method, the + * method has no code model, or the generated target type differs + * from {@code methodType} + */ + public static CallSite codeModelExecutor(MethodHandles.Lookup lookup, + String name, + MethodType methodType, + MethodHandle method) { + Objects.requireNonNull(name); + Objects.requireNonNull(methodType); + Member member = lookup.revealDirect(method).reflectAs(Member.class, lookup); + if (!(member instanceof Method m)) { + throw new IllegalArgumentException("Handle does not reference a method"); + } + CoreOp.FuncOp model = Op.ofMethod(m).orElseThrow(() -> + new IllegalArgumentException("Method has no code model: " + m)); + MethodHandle target = BytecodeGenerator.generate(lookup, model); + if (!target.type().equals(methodType)) { + throw new IllegalArgumentException("Code model target type " + target.type() + + " differs from call-site type " + methodType); + } + return new ConstantCallSite(target); + } + + /** + * Bootstrap method for linking an {@code invokedynamic} call site whose + * target creates reflectable lambda instances, using the lambda's stored + * code model as its implementation. + *
+ * Except for the implementation method handle, this method follows the + * contract and encoded-name convention of + * {@link ReflectableLambdaMetafactory#metafactory(Lookup, String, + * MethodType, MethodType, MethodHandle, MethodType)}. + *
+ * The supplied implementation method handle is ignored and replaced + * with one linked from the stored code model. + * + * @param caller the lookup + * @param interfaceMethodName the encoded interface-method and code-model + * accessor names, as specified by + * {@link ReflectableLambdaMetafactory#metafactory(Lookup, + * String, MethodType, MethodType, MethodHandle, + * MethodType)} + * @param factoryType The expected signature of the {@code CallSite}. + * @param interfaceMethodType Signature and return type of method to be + * implemented by the function object. + * @param implementation ignored, retained for compatibility with the + standard lambda metafactory bootstrap signature + * @param dynamicMethodType The signature and return type that should + * be enforced dynamically at invocation time. + * @return a call site whose target creates reflectable lambda instances of + * the functional interface specified by the return type of + * {@code factoryType}; each instance can be inspected using + * {@link Op#ofLambda(Object)} + * + * @throws LambdaConversionException if the implementation cannot be linked + * from its stored code model, or if, after the lambda name is + * decoded, the parameters of the call are invalid for + * {@link ReflectableLambdaMetafactory#metafactory(Lookup, String, + * MethodType, MethodType, MethodHandle, MethodType)} + * @throws NullPointerException if {@code interfaceMethodName}, + * {@code factoryType}, {@code interfaceMethodType}, or + * {@code dynamicMethodType} is {@code null} + * + * @see ReflectableLambdaMetafactory#metafactory(Lookup, String, MethodType, + * MethodType, MethodHandle, MethodType) + * @see Op#ofLambda(Object) + */ + public static CallSite lambdaMetafactory(MethodHandles.Lookup caller, + String interfaceMethodName, + MethodType factoryType, + MethodType interfaceMethodType, + MethodHandle implementation, + MethodType dynamicMethodType) throws LambdaConversionException { + MethodHandle generatedImpl = linkLambdaImplementation(caller, interfaceMethodName); + CallSite site = ReflectableLambdaMetafactory.metafactory(caller, + interfaceMethodName, + generatedFactoryType(factoryType, generatedImpl), + interfaceMethodType, + generatedImpl, + dynamicMethodType); + return new ConstantCallSite(site.getTarget().asType(factoryType)); + } + + /** + * Bootstrap method for linking an {@code invokedynamic} call site whose + * target creates reflectable lambda instances, using the lambda's stored + * code model as its implementation. + *
+ * Except for the implementation method handle, this method follows the + * contract and encoded-name convention of + * {@link ReflectableLambdaMetafactory#altMetafactory(Lookup, String, + * MethodType, Object...)}. + *
+ * The implementation method handle in {@code args} is replaced with one
+ * linked from the stored code model.
+ *
+ * @param caller the lookup
+ * @param interfaceMethodName the encoded interface-method and code-model
+ * accessor names, as specified by
+ * {@link ReflectableLambdaMetafactory#altMetafactory(Lookup,
+ * String, MethodType, Object...)}
+ * @param factoryType The expected signature of the {@code CallSite}.
+ * @param args An array of {@code Object} containing the required
+ * arguments {@code interfaceMethodType}, {@code implementation},
+ * {@code dynamicMethodType}, {@code flags}, and any
+ * optional arguments, as required by
+ * {@link ReflectableLambdaMetafactory#altMetafactory(Lookup,
+ * String, MethodType, Object...)}, the
+ * {@code implementation} component is ignored and replaced
+ * with one linked from the stored code model
+ * @return a CallSite whose target can be used to perform capture, generating
+ * a reflectable lambda instance implementing the functional
+ * interface specified by the return type of {@code factoryType}.
+ * The code model for such instance can be inspected using
+ * {@link Op#ofLambda(Object)}.
+ *
+ * @throws LambdaConversionException if the implementation cannot be linked
+ * from its stored code model, or if, after the lambda name is
+ * decoded, the parameters of the call are invalid for
+ * {@link ReflectableLambdaMetafactory#altMetafactory(Lookup, String,
+ * MethodType, Object...)}
+ * @throws NullPointerException if {@code interfaceMethodName},
+ * {@code factoryType}, or {@code args} is {@code null}, or if a
+ * required component of {@code args} other than
+ * {@code implementation} is {@code null}
+ * @throws IllegalArgumentException If {@code args} are invalid for
+ * {@link ReflectableLambdaMetafactory#altMetafactory(Lookup, String,
+ * MethodType, Object...)}
+ *
+ * @see ReflectableLambdaMetafactory#altMetafactory(Lookup, String, MethodType,
+ * Object...)
+ * @see Op#ofLambda(Object)
+ */
+ public static CallSite lambdaAltMetafactory(MethodHandles.Lookup caller,
+ String interfaceMethodName,
+ MethodType factoryType,
+ Object... args) throws LambdaConversionException {
+ MethodHandle generatedImpl = linkLambdaImplementation(caller, interfaceMethodName);
+ args[1] = generatedImpl;
+ CallSite site = ReflectableLambdaMetafactory.altMetafactory(caller,
+ interfaceMethodName,
+ generatedFactoryType(factoryType, generatedImpl),
+ args);
+ return new ConstantCallSite(site.getTarget().asType(factoryType));
+ }
+
+ private static MethodType generatedFactoryType(MethodType factoryType, MethodHandle implementation) {
+ MethodType implementationType = implementation.type();
+ return implementationType.dropParameterTypes(factoryType.parameterCount(), implementationType.parameterCount())
+ .changeReturnType(factoryType.returnType());
+ }
+
+ private static MethodHandle linkLambdaImplementation(MethodHandles.Lookup caller,
+ String interfaceMethodName)
+ throws LambdaConversionException {
+ String modelMethodName = interfaceMethodName.split("=")[1];
+ try {
+ MethodHandle opHandle = caller.findStatic(caller.lookupClass(),
+ modelMethodName,
+ MethodType.methodType(Op.class));
+ MethodHandle methodHandle = BytecodeGenerator.generate(caller,
+ unquoteLambda((CoreOp.FuncOp)opHandle.invoke()));
+ return methodHandle;
+ } catch (Throwable t) {
+ throw new LambdaConversionException(t);
+ }
+ }
+
+ // flatten the quoted lambda into the enclosing function model
+ private static CoreOp.FuncOp unquoteLambda(CoreOp.FuncOp funcOp) {
+ int capturedValues = funcOp.parameters().size();
+ List