1 /*
2  * Copyright (c) 2018, 2021, 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.constant;
26 
27 import java.lang.Enum.EnumDesc;
28 import java.lang.invoke.MethodHandle;
29 import java.lang.invoke.MethodHandles;
30 import java.lang.invoke.VarHandle;
31 import java.lang.invoke.VarHandle.VarHandleDesc;
32 import java.util.Arrays;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Objects;
36 import java.util.function.Function;
37 import java.util.stream.Stream;
38 
39 import static java.lang.constant.ConstantDescs.CD_Class;
40 import static java.lang.constant.ConstantDescs.CD_VarHandle;
41 import static java.lang.constant.ConstantDescs.DEFAULT_NAME;
42 import static java.lang.constant.ConstantUtils.EMPTY_CONSTANTDESC;
43 import static java.lang.constant.ConstantUtils.validateMemberName;
44 import static java.util.Objects.requireNonNull;
45 import static java.util.stream.Collectors.joining;
46 
47 /**
48  * A <a href="package-summary.html#nominal">nominal descriptor</a> for a
49  * dynamic constant (one described in the constant pool with
50  * {@code Constant_Dynamic_info}.)
51  *
52  * <p>Concrete subtypes of {@linkplain DynamicConstantDesc} should be immutable
53  * and their behavior should not rely on object identity.
54  *
55  * @param <T> the type of the dynamic constant
56  *
57  * @since 12
58  */
59 public abstract non-sealed class DynamicConstantDesc<T>
60         implements ConstantDesc {
61 
62     private final DirectMethodHandleDesc bootstrapMethod;
63     private final ConstantDesc[] bootstrapArgs;
64     private final String constantName;
65     private final ClassDesc constantType;
66 
67     /**
68      * Creates a nominal descriptor for a dynamic constant.
69      *
70      * @param bootstrapMethod a {@link DirectMethodHandleDesc} describing the
71      *                        bootstrap method for the constant
72      * @param constantName The unqualified name that would appear in the {@code NameAndType}
73      *                     operand of the {@code LDC} for this constant
74      * @param constantType a {@link ClassDesc} describing the type
75      *                     that would appear in the {@code NameAndType} operand
76      *                     of the {@code LDC} for this constant
77      * @param bootstrapArgs {@link ConstantDesc}s describing the static arguments
78      *                      to the bootstrap, that would appear in the
79      *                      {@code BootstrapMethods} attribute
80      * @throws NullPointerException if any argument is null
81      * @throws IllegalArgumentException if the {@code name} has the incorrect
82      * format
83      * @jvms 4.2.2 Unqualified Names
84      */
DynamicConstantDesc(DirectMethodHandleDesc bootstrapMethod, String constantName, ClassDesc constantType, ConstantDesc... bootstrapArgs)85     protected DynamicConstantDesc(DirectMethodHandleDesc bootstrapMethod,
86                                   String constantName,
87                                   ClassDesc constantType,
88                                   ConstantDesc... bootstrapArgs) {
89         this.bootstrapMethod = requireNonNull(bootstrapMethod);
90         this.constantName = validateMemberName(requireNonNull(constantName), true);
91         this.constantType = requireNonNull(constantType);
92         this.bootstrapArgs = requireNonNull(bootstrapArgs).clone();
93 
94         if (constantName.length() == 0)
95             throw new IllegalArgumentException("Illegal invocation name: " + constantName);
96     }
97 
98     /**
99      * Returns a nominal descriptor for a dynamic constant, transforming it into
100      * a more specific type if the constant bootstrap is a well-known one and a
101      * more specific nominal descriptor type (e.g., ClassDesc) is available.
102      *
103      * <p>Classes whose {@link Constable#describeConstable()} method produce
104      * a {@linkplain DynamicConstantDesc} with a well-known bootstrap including
105      * {@link Class} (for instances describing primitive types), {@link Enum},
106      * and {@link VarHandle}.
107      *
108      * <p>Bytecode-reading APIs that process the constant pool and wish to expose
109      * entries as {@link ConstantDesc} to their callers should generally use this
110      * method in preference to {@link #ofNamed(DirectMethodHandleDesc, String, ClassDesc, ConstantDesc...)}
111      * because this may result in a more specific type that can be provided to
112      * callers.
113      *
114      * @param <T> the type of the dynamic constant
115      * @param bootstrapMethod a {@link DirectMethodHandleDesc} describing the
116      *                        bootstrap method for the constant
117      * @param constantName The unqualified name that would appear in the {@code NameAndType}
118      *                     operand of the {@code LDC} for this constant
119      * @param constantType a {@link ClassDesc} describing the type
120      *                     that would appear in the {@code NameAndType} operand
121      *                     of the {@code LDC} for this constant
122      * @param bootstrapArgs {@link ConstantDesc}s describing the static arguments
123      *                      to the bootstrap, that would appear in the
124      *                      {@code BootstrapMethods} attribute
125      * @return the nominal descriptor
126      * @throws NullPointerException if any argument is null
127      * @throws IllegalArgumentException if the {@code name} has the incorrect
128      * format
129      * @jvms 4.2.2 Unqualified Names
130      */
131     // Do not call this method from the static initialization of java.lang.constant.ConstantDescs
132     // since that can lead to potential deadlock during multi-threaded concurrent execution
ofCanonical(DirectMethodHandleDesc bootstrapMethod, String constantName, ClassDesc constantType, ConstantDesc[] bootstrapArgs)133     public static<T> ConstantDesc ofCanonical(DirectMethodHandleDesc bootstrapMethod,
134                                               String constantName,
135                                               ClassDesc constantType,
136                                               ConstantDesc[] bootstrapArgs) {
137         return DynamicConstantDesc.<T>ofNamed(bootstrapMethod, constantName, constantType, bootstrapArgs)
138                 .tryCanonicalize();
139     }
140 
141     /**
142      * Returns a nominal descriptor for a dynamic constant.
143      *
144      * @param <T> the type of the dynamic constant
145      * @param bootstrapMethod a {@link DirectMethodHandleDesc} describing the
146      *                        bootstrap method for the constant
147      * @param constantName The unqualified name that would appear in the {@code NameAndType}
148      *                     operand of the {@code LDC} for this constant
149      * @param constantType a {@link ClassDesc} describing the type
150      *                     that would appear in the {@code NameAndType} operand
151      *                     of the {@code LDC} for this constant
152      * @param bootstrapArgs {@link ConstantDesc}s describing the static arguments
153      *                      to the bootstrap, that would appear in the
154      *                      {@code BootstrapMethods} attribute
155      * @return the nominal descriptor
156      * @throws NullPointerException if any argument is null
157      * @throws IllegalArgumentException if the {@code name} has the incorrect
158      * format
159      * @jvms 4.2.2 Unqualified Names
160      */
161 
ofNamed(DirectMethodHandleDesc bootstrapMethod, String constantName, ClassDesc constantType, ConstantDesc... bootstrapArgs)162     public static<T> DynamicConstantDesc<T> ofNamed(DirectMethodHandleDesc bootstrapMethod,
163                                                     String constantName,
164                                                     ClassDesc constantType,
165                                                     ConstantDesc... bootstrapArgs) {
166         return new AnonymousDynamicConstantDesc<>(bootstrapMethod, constantName, constantType, bootstrapArgs);
167     }
168 
169     /**
170      * Returns a nominal descriptor for a dynamic constant whose name parameter
171      * is {@link ConstantDescs#DEFAULT_NAME}, and whose type parameter is always
172      * the same as the bootstrap method return type.
173      *
174      * @param <T> the type of the dynamic constant
175      * @param bootstrapMethod a {@link DirectMethodHandleDesc} describing the
176      *                        bootstrap method for the constant
177      * @param bootstrapArgs {@link ConstantDesc}s describing the static arguments
178      *                      to the bootstrap, that would appear in the
179      *                      {@code BootstrapMethods} attribute
180      * @return the nominal descriptor
181      * @throws NullPointerException if any argument is null
182      * @jvms 4.2.2 Unqualified Names
183      */
of(DirectMethodHandleDesc bootstrapMethod, ConstantDesc... bootstrapArgs)184     public static<T> DynamicConstantDesc<T> of(DirectMethodHandleDesc bootstrapMethod,
185                                                ConstantDesc... bootstrapArgs) {
186         return ofNamed(bootstrapMethod, DEFAULT_NAME, bootstrapMethod.invocationType().returnType(), bootstrapArgs);
187     }
188 
189     /**
190      * Returns a nominal descriptor for a dynamic constant whose bootstrap has
191      * no static arguments, whose name parameter is {@link ConstantDescs#DEFAULT_NAME},
192      * and whose type parameter is always the same as the bootstrap method return type.
193      *
194      * @param <T> the type of the dynamic constant
195      * @param bootstrapMethod a {@link DirectMethodHandleDesc} describing the
196      *                        bootstrap method for the constant
197      * @return the nominal descriptor
198      * @throws NullPointerException if any argument is null
199      */
of(DirectMethodHandleDesc bootstrapMethod)200     public static<T> DynamicConstantDesc<T> of(DirectMethodHandleDesc bootstrapMethod) {
201         return of(bootstrapMethod, EMPTY_CONSTANTDESC);
202     }
203 
204     /**
205      * Returns the name that would appear in the {@code NameAndType} operand
206      * of the {@code LDC} for this constant.
207      *
208      * @return the constant name
209      */
constantName()210     public String constantName() {
211         return constantName;
212     }
213 
214     /**
215      * Returns a {@link ClassDesc} describing the type that would appear in the
216      * {@code NameAndType} operand of the {@code LDC} for this constant.
217      *
218      * @return the constant type
219      */
constantType()220     public ClassDesc constantType() {
221         return constantType;
222     }
223 
224     /**
225      * Returns a {@link MethodHandleDesc} describing the bootstrap method for
226      * this constant.
227      *
228      * @return the bootstrap method
229      */
bootstrapMethod()230     public DirectMethodHandleDesc bootstrapMethod() {
231         return bootstrapMethod;
232     }
233 
234     /**
235      * Returns the bootstrap arguments for this constant.
236      *
237      * @return the bootstrap arguments
238      */
bootstrapArgs()239     public ConstantDesc[] bootstrapArgs() {
240         return bootstrapArgs.clone();
241     }
242 
243     /**
244      * Returns the bootstrap arguments for this constant as an immutable {@link List}.
245      *
246      * @return a {@link List} of the bootstrap arguments
247      */
bootstrapArgsList()248     public List<ConstantDesc> bootstrapArgsList() {
249         return List.of(bootstrapArgs);
250     }
251 
252     @SuppressWarnings("unchecked")
resolveConstantDesc(MethodHandles.Lookup lookup)253     public T resolveConstantDesc(MethodHandles.Lookup lookup) throws ReflectiveOperationException {
254         try {
255             MethodHandle bsm = (MethodHandle) bootstrapMethod.resolveConstantDesc(lookup);
256             if (bsm.type().parameterCount() < 2 ||
257                 !MethodHandles.Lookup.class.isAssignableFrom(bsm.type().parameterType(0))) {
258                 throw new BootstrapMethodError(
259                         "Invalid bootstrap method declared for resolving a dynamic constant: " + bootstrapMethod);
260             }
261             Object[] bsmArgs = new Object[3 + bootstrapArgs.length];
262             bsmArgs[0] = lookup;
263             bsmArgs[1] = constantName;
264             bsmArgs[2] = constantType.resolveConstantDesc(lookup);
265             for (int i = 0; i < bootstrapArgs.length; i++)
266                 bsmArgs[3 + i] = bootstrapArgs[i].resolveConstantDesc(lookup);
267 
268             return (T) bsm.invokeWithArguments(bsmArgs);
269         } catch (Error e) {
270             throw e;
271         } catch (Throwable t) {
272             throw new BootstrapMethodError(t);
273         }
274     }
275 
tryCanonicalize()276     private ConstantDesc tryCanonicalize() {
277         Function<DynamicConstantDesc<?>, ConstantDesc> f = CanonicalMapHolder.CANONICAL_MAP.get(bootstrapMethod);
278         if (f != null) {
279             try {
280                 return f.apply(this);
281             }
282             catch (Throwable t) {
283                 return this;
284             }
285         }
286         return this;
287     }
288 
canonicalizeNull(DynamicConstantDesc<?> desc)289     private static ConstantDesc canonicalizeNull(DynamicConstantDesc<?> desc) {
290         if (desc.bootstrapArgs.length != 0)
291             return desc;
292         return ConstantDescs.NULL;
293     }
294 
canonicalizeEnum(DynamicConstantDesc<?> desc)295     private static ConstantDesc canonicalizeEnum(DynamicConstantDesc<?> desc) {
296         if (desc.bootstrapArgs.length != 0
297             || desc.constantName == null)
298             return desc;
299         return EnumDesc.of(desc.constantType, desc.constantName);
300     }
301 
canonicalizePrimitiveClass(DynamicConstantDesc<?> desc)302     private static ConstantDesc canonicalizePrimitiveClass(DynamicConstantDesc<?> desc) {
303         if (desc.bootstrapArgs.length != 0
304             || !desc.constantType().equals(CD_Class)
305             || desc.constantName == null)
306             return desc;
307         return ClassDesc.ofDescriptor(desc.constantName);
308     }
309 
canonicalizeStaticFieldVarHandle(DynamicConstantDesc<?> desc)310     private static ConstantDesc canonicalizeStaticFieldVarHandle(DynamicConstantDesc<?> desc) {
311         if (desc.bootstrapArgs.length != 2
312                 || !desc.constantType().equals(CD_VarHandle))
313             return desc;
314         return VarHandleDesc.ofStaticField((ClassDesc) desc.bootstrapArgs[0],
315                                      desc.constantName,
316                                      (ClassDesc) desc.bootstrapArgs[1]);
317     }
318 
canonicalizeFieldVarHandle(DynamicConstantDesc<?> desc)319     private static ConstantDesc canonicalizeFieldVarHandle(DynamicConstantDesc<?> desc) {
320         if (desc.bootstrapArgs.length != 2
321             || !desc.constantType().equals(CD_VarHandle))
322             return desc;
323         return VarHandleDesc.ofField((ClassDesc) desc.bootstrapArgs[0],
324                                      desc.constantName,
325                                      (ClassDesc) desc.bootstrapArgs[1]);
326     }
327 
canonicalizeArrayVarHandle(DynamicConstantDesc<?> desc)328     private static ConstantDesc canonicalizeArrayVarHandle(DynamicConstantDesc<?> desc) {
329         if (desc.bootstrapArgs.length != 1
330             || !desc.constantType().equals(CD_VarHandle))
331             return desc;
332         return VarHandleDesc.ofArray((ClassDesc) desc.bootstrapArgs[0]);
333     }
334 
335     // @@@ To eventually support in canonicalization: DCR with BSM=MHR_METHODHANDLEDESC_ASTYPE becomes AsTypeMHDesc
336 
337     /**
338      * Compares the specified object with this descriptor for equality.  Returns
339      * {@code true} if and only if the specified object is also a
340      * {@linkplain DynamicConstantDesc}, and both descriptors have equal
341      * bootstrap methods, bootstrap argument lists, constant name, and
342      * constant type.
343      *
344      * @param o the {@code DynamicConstantDesc} to compare to this
345      *       {@code DynamicConstantDesc}
346      * @return {@code true} if the specified {@code DynamicConstantDesc}
347      *      is equal to this {@code DynamicConstantDesc}.
348      *
349      */
350     @Override
equals(Object o)351     public final boolean equals(Object o) {
352         if (this == o) return true;
353         return (o instanceof DynamicConstantDesc<?> desc)
354                 && Objects.equals(bootstrapMethod, desc.bootstrapMethod)
355                 && Arrays.equals(bootstrapArgs, desc.bootstrapArgs)
356                 && Objects.equals(constantName, desc.constantName)
357                 && Objects.equals(constantType, desc.constantType);
358     }
359 
360     @Override
hashCode()361     public final int hashCode() {
362         int result = Objects.hash(bootstrapMethod, constantName, constantType);
363         result = 31 * result + Arrays.hashCode(bootstrapArgs);
364         return result;
365     }
366 
367     /**
368      * Returns a compact textual description of this constant description,
369      * including the bootstrap method, the constant name and type, and
370      * the static bootstrap arguments.
371      *
372      * @return A compact textual description of this call site descriptor
373      */
374     @Override
toString()375     public String toString() {
376         return String.format("DynamicConstantDesc[%s::%s(%s%s)%s]",
377                              bootstrapMethod.owner().displayName(),
378                              bootstrapMethod.methodName(),
379                              constantName.equals(ConstantDescs.DEFAULT_NAME) ? "" : constantName + "/",
380                              Stream.of(bootstrapArgs).map(Object::toString).collect(joining(",")),
381                              constantType.displayName());
382     }
383 
384     private static class AnonymousDynamicConstantDesc<T> extends DynamicConstantDesc<T> {
AnonymousDynamicConstantDesc(DirectMethodHandleDesc bootstrapMethod, String constantName, ClassDesc constantType, ConstantDesc... bootstrapArgs)385         AnonymousDynamicConstantDesc(DirectMethodHandleDesc bootstrapMethod, String constantName, ClassDesc constantType, ConstantDesc... bootstrapArgs) {
386             super(bootstrapMethod, constantName, constantType, bootstrapArgs);
387         }
388     }
389 
390     private static final class CanonicalMapHolder {
391         static final Map<MethodHandleDesc, Function<DynamicConstantDesc<?>, ConstantDesc>> CANONICAL_MAP =
392                 Map.ofEntries(
393                     Map.entry(ConstantDescs.BSM_PRIMITIVE_CLASS, DynamicConstantDesc::canonicalizePrimitiveClass),
394                     Map.entry(ConstantDescs.BSM_ENUM_CONSTANT, DynamicConstantDesc::canonicalizeEnum),
395                     Map.entry(ConstantDescs.BSM_NULL_CONSTANT, DynamicConstantDesc::canonicalizeNull),
396                     Map.entry(ConstantDescs.BSM_VARHANDLE_STATIC_FIELD, DynamicConstantDesc::canonicalizeStaticFieldVarHandle),
397                     Map.entry(ConstantDescs.BSM_VARHANDLE_FIELD, DynamicConstantDesc::canonicalizeFieldVarHandle),
398                     Map.entry(ConstantDescs.BSM_VARHANDLE_ARRAY, DynamicConstantDesc::canonicalizeArrayVarHandle));
399     }
400 }
401