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