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 }