1 /*
2  * Copyright (c) 2017, 2018, 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.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 package java.lang.invoke;
26 
27 import sun.invoke.util.Wrapper;
28 
29 import static java.lang.invoke.MethodHandleNatives.mapLookupExceptionToError;
30 import static java.util.Objects.requireNonNull;
31 
32 /**
33  * Bootstrap methods for dynamically-computed constants.
34  *
35  * <p>The bootstrap methods in this class will throw a
36  * {@code NullPointerException} for any reference argument that is {@code null},
37  * unless the argument is specified to be unused or specified to accept a
38  * {@code null} value.
39  *
40  * @since 11
41  */
42 public final class ConstantBootstraps {
43     // implements the upcall from the JVM, MethodHandleNatives.linkDynamicConstant:
44     /*non-public*/
makeConstant(MethodHandle bootstrapMethod, String name, Class<?> type, Object info, Class<?> callerClass)45     static Object makeConstant(MethodHandle bootstrapMethod,
46                                // Callee information:
47                                String name, Class<?> type,
48                                // Extra arguments for BSM, if any:
49                                Object info,
50                                // Caller information:
51                                Class<?> callerClass) {
52         // Restrict bootstrap methods to those whose first parameter is Lookup
53         // The motivation here is, in the future, to possibly support BSMs
54         // that do not accept the meta-data of lookup/name/type, thereby
55         // allowing the co-opting of existing methods to be used as BSMs as
56         // long as the static arguments can be passed as method arguments
57         MethodType mt = bootstrapMethod.type();
58         if (mt.parameterCount() < 2 ||
59             !MethodHandles.Lookup.class.isAssignableFrom(mt.parameterType(0))) {
60             throw new BootstrapMethodError(
61                     "Invalid bootstrap method declared for resolving a dynamic constant: " + bootstrapMethod);
62         }
63 
64         // BSMI.invoke handles all type checking and exception translation.
65         // If type is not a reference type, the JVM is expecting a boxed
66         // version, and will manage unboxing on the other side.
67         return BootstrapMethodInvoker.invoke(
68                 type, bootstrapMethod, name, type, info, callerClass);
69     }
70 
71     /**
72      * Returns a {@code null} object reference for the reference type specified
73      * by {@code type}.
74      *
75      * @param lookup unused
76      * @param name unused
77      * @param type a reference type
78      * @return a {@code null} value
79      * @throws IllegalArgumentException if {@code type} is not a reference type
80      */
nullConstant(MethodHandles.Lookup lookup, String name, Class<?> type)81     public static Object nullConstant(MethodHandles.Lookup lookup, String name, Class<?> type) {
82         if (requireNonNull(type).isPrimitive()) {
83             throw new IllegalArgumentException(String.format("not reference: %s", type));
84         }
85 
86         return null;
87     }
88 
89     /**
90      * Returns a {@link Class} mirror for the primitive type whose type
91      * descriptor is specified by {@code name}.
92      *
93      * @param lookup unused
94      * @param name the descriptor (JVMS 4.3) of the desired primitive type
95      * @param type the required result type (must be {@code Class.class})
96      * @return the {@link Class} mirror
97      * @throws IllegalArgumentException if the name is not a descriptor for a
98      * primitive type or the type is not {@code Class.class}
99      */
primitiveClass(MethodHandles.Lookup lookup, String name, Class<?> type)100     public static Class<?> primitiveClass(MethodHandles.Lookup lookup, String name, Class<?> type) {
101         requireNonNull(name);
102         requireNonNull(type);
103         if (type != Class.class) {
104             throw new IllegalArgumentException();
105         }
106         if (name.length() != 1) {
107             throw new IllegalArgumentException(String.format("not primitive: %s", name));
108         }
109 
110         return Wrapper.forPrimitiveType(name.charAt(0)).primitiveType();
111     }
112 
113     /**
114      * Returns an {@code enum} constant of the type specified by {@code type}
115      * with the name specified by {@code name}.
116      *
117      * @param lookup the lookup context describing the class performing the
118      * operation (normally stacked by the JVM)
119      * @param name the name of the constant to return, which must exactly match
120      * an enum constant in the specified type.
121      * @param type the {@code Class} object describing the enum type for which
122      * a constant is to be returned
123      * @param <E> The enum type for which a constant value is to be returned
124      * @return the enum constant of the specified enum type with the
125      * specified name
126      * @throws IllegalAccessError if the declaring class or the field is not
127      * accessible to the class performing the operation
128      * @throws IllegalArgumentException if the specified enum type has
129      * no constant with the specified name, or the specified
130      * class object does not represent an enum type
131      * @see Enum#valueOf(Class, String)
132      */
enumConstant(MethodHandles.Lookup lookup, String name, Class<E> type)133     public static <E extends Enum<E>> E enumConstant(MethodHandles.Lookup lookup, String name, Class<E> type) {
134         requireNonNull(lookup);
135         requireNonNull(name);
136         requireNonNull(type);
137         validateClassAccess(lookup, type);
138 
139         return Enum.valueOf(type, name);
140     }
141 
142     /**
143      * Returns the value of a static final field.
144      *
145      * @param lookup the lookup context describing the class performing the
146      * operation (normally stacked by the JVM)
147      * @param name the name of the field
148      * @param type the type of the field
149      * @param declaringClass the class in which the field is declared
150      * @return the value of the field
151      * @throws IllegalAccessError if the declaring class or the field is not
152      * accessible to the class performing the operation
153      * @throws NoSuchFieldError if the specified field does not exist
154      * @throws IncompatibleClassChangeError if the specified field is not
155      * {@code final}
156      */
getStaticFinal(MethodHandles.Lookup lookup, String name, Class<?> type, Class<?> declaringClass)157     public static Object getStaticFinal(MethodHandles.Lookup lookup, String name, Class<?> type,
158                                         Class<?> declaringClass) {
159         requireNonNull(lookup);
160         requireNonNull(name);
161         requireNonNull(type);
162         requireNonNull(declaringClass);
163 
164         MethodHandle mh;
165         try {
166             mh = lookup.findStaticGetter(declaringClass, name, type);
167             MemberName member = mh.internalMemberName();
168             if (!member.isFinal()) {
169                 throw new IncompatibleClassChangeError("not a final field: " + name);
170             }
171         }
172         catch (ReflectiveOperationException ex) {
173             throw mapLookupExceptionToError(ex);
174         }
175 
176         // Since mh is a handle to a static field only instances of
177         // VirtualMachineError are anticipated to be thrown, such as a
178         // StackOverflowError or an InternalError from the j.l.invoke code
179         try {
180             return mh.invoke();
181         }
182         catch (RuntimeException | Error e) {
183             throw e;
184         }
185         catch (Throwable e) {
186             throw new LinkageError("Unexpected throwable", e);
187         }
188     }
189 
190     /**
191      * Returns the value of a static final field declared in the class which
192      * is the same as the field's type (or, for primitive-valued fields,
193      * declared in the wrapper class.)  This is a simplified form of
194      * {@link #getStaticFinal(MethodHandles.Lookup, String, Class, Class)}
195      * for the case where a class declares distinguished constant instances of
196      * itself.
197      *
198      * @param lookup the lookup context describing the class performing the
199      * operation (normally stacked by the JVM)
200      * @param name the name of the field
201      * @param type the type of the field
202      * @return the value of the field
203      * @throws IllegalAccessError if the declaring class or the field is not
204      * accessible to the class performing the operation
205      * @throws NoSuchFieldError if the specified field does not exist
206      * @throws IncompatibleClassChangeError if the specified field is not
207      * {@code final}
208      * @see #getStaticFinal(MethodHandles.Lookup, String, Class, Class)
209      */
getStaticFinal(MethodHandles.Lookup lookup, String name, Class<?> type)210     public static Object getStaticFinal(MethodHandles.Lookup lookup, String name, Class<?> type) {
211         requireNonNull(type);
212 
213         Class<?> declaring = type.isPrimitive()
214                              ? Wrapper.forPrimitiveType(type).wrapperType()
215                              : type;
216         return getStaticFinal(lookup, name, type, declaring);
217     }
218 
219 
220     /**
221      * Returns the result of invoking a method handle with the provided
222      * arguments.
223      * <p>
224      * This method behaves as if the method handle to be invoked is the result
225      * of adapting the given method handle, via {@link MethodHandle#asType}, to
226      * adjust the return type to the desired type.
227      *
228      * @param lookup unused
229      * @param name unused
230      * @param type the desired type of the value to be returned, which must be
231      * compatible with the return type of the method handle
232      * @param handle the method handle to be invoked
233      * @param args the arguments to pass to the method handle, as if with
234      * {@link MethodHandle#invokeWithArguments}.  Each argument may be
235      * {@code null}.
236      * @return the result of invoking the method handle
237      * @throws WrongMethodTypeException if the handle's method type cannot be
238      * adjusted to take the given number of arguments, or if the handle's return
239      * type cannot be adjusted to the desired type
240      * @throws ClassCastException if an argument or the result produced by
241      * invoking the handle cannot be converted by reference casting
242      * @throws Throwable anything thrown by the method handle invocation
243      */
invoke(MethodHandles.Lookup lookup, String name, Class<?> type, MethodHandle handle, Object... args)244     public static Object invoke(MethodHandles.Lookup lookup, String name, Class<?> type,
245                                 MethodHandle handle, Object... args) throws Throwable {
246         requireNonNull(type);
247         requireNonNull(handle);
248         requireNonNull(args);
249 
250         if (type != handle.type().returnType()) {
251             // Adjust the return type of the handle to be invoked while
252             // preserving variable arity if present
253             handle = handle.asType(handle.type().changeReturnType(type)).
254                     withVarargs(handle.isVarargsCollector());
255         }
256 
257         return handle.invokeWithArguments(args);
258     }
259 
260     /**
261      * Finds a {@link VarHandle} for an instance field.
262      *
263      * @param lookup the lookup context describing the class performing the
264      * operation (normally stacked by the JVM)
265      * @param name the name of the field
266      * @param type the required result type (must be {@code Class<VarHandle>})
267      * @param declaringClass the class in which the field is declared
268      * @param fieldType the type of the field
269      * @return the {@link VarHandle}
270      * @throws IllegalAccessError if the declaring class or the field is not
271      * accessible to the class performing the operation
272      * @throws NoSuchFieldError if the specified field does not exist
273      * @throws IllegalArgumentException if the type is not {@code VarHandle}
274      */
fieldVarHandle(MethodHandles.Lookup lookup, String name, Class<VarHandle> type, Class<?> declaringClass, Class<?> fieldType)275     public static VarHandle fieldVarHandle(MethodHandles.Lookup lookup, String name, Class<VarHandle> type,
276                                            Class<?> declaringClass, Class<?> fieldType) {
277         requireNonNull(lookup);
278         requireNonNull(name);
279         requireNonNull(type);
280         requireNonNull(declaringClass);
281         requireNonNull(fieldType);
282         if (type != VarHandle.class) {
283             throw new IllegalArgumentException();
284         }
285 
286         try {
287             return lookup.findVarHandle(declaringClass, name, fieldType);
288         }
289         catch (ReflectiveOperationException e) {
290             throw mapLookupExceptionToError(e);
291         }
292     }
293 
294     /**
295      * Finds a {@link VarHandle} for a static field.
296      *
297      * @param lookup the lookup context describing the class performing the
298      * operation (normally stacked by the JVM)
299      * @param name the name of the field
300      * @param type the required result type (must be {@code Class<VarHandle>})
301      * @param declaringClass the class in which the field is declared
302      * @param fieldType the type of the field
303      * @return the {@link VarHandle}
304      * @throws IllegalAccessError if the declaring class or the field is not
305      * accessible to the class performing the operation
306      * @throws NoSuchFieldError if the specified field does not exist
307      * @throws IllegalArgumentException if the type is not {@code VarHandle}
308      */
staticFieldVarHandle(MethodHandles.Lookup lookup, String name, Class<VarHandle> type, Class<?> declaringClass, Class<?> fieldType)309     public static VarHandle staticFieldVarHandle(MethodHandles.Lookup lookup, String name, Class<VarHandle> type,
310                                                  Class<?> declaringClass, Class<?> fieldType) {
311         requireNonNull(lookup);
312         requireNonNull(name);
313         requireNonNull(type);
314         requireNonNull(declaringClass);
315         requireNonNull(fieldType);
316         if (type != VarHandle.class) {
317             throw new IllegalArgumentException();
318         }
319 
320         try {
321             return lookup.findStaticVarHandle(declaringClass, name, fieldType);
322         }
323         catch (ReflectiveOperationException e) {
324             throw mapLookupExceptionToError(e);
325         }
326     }
327 
328     /**
329      * Finds a {@link VarHandle} for an array type.
330      *
331      * @param lookup the lookup context describing the class performing the
332      * operation (normally stacked by the JVM)
333      * @param name unused
334      * @param type the required result type (must be {@code Class<VarHandle>})
335      * @param arrayClass the type of the array
336      * @return the {@link VarHandle}
337      * @throws IllegalAccessError if the component type of the array is not
338      * accessible to the class performing the operation
339      * @throws IllegalArgumentException if the type is not {@code VarHandle}
340      */
arrayVarHandle(MethodHandles.Lookup lookup, String name, Class<VarHandle> type, Class<?> arrayClass)341     public static VarHandle arrayVarHandle(MethodHandles.Lookup lookup, String name, Class<VarHandle> type,
342                                            Class<?> arrayClass) {
343         requireNonNull(lookup);
344         requireNonNull(type);
345         requireNonNull(arrayClass);
346         if (type != VarHandle.class) {
347             throw new IllegalArgumentException();
348         }
349 
350         return MethodHandles.arrayElementVarHandle(validateClassAccess(lookup, arrayClass));
351     }
352 
validateClassAccess(MethodHandles.Lookup lookup, Class<T> type)353     private static <T> Class<T> validateClassAccess(MethodHandles.Lookup lookup, Class<T> type) {
354         try {
355             lookup.accessClass(type);
356             return type;
357         }
358         catch (ReflectiveOperationException ex) {
359             throw mapLookupExceptionToError(ex);
360         }
361     }
362 }
363