| 
/*  | 
 | 
 * Copyright (c) 2018, 2021, 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.  Oracle designates this  | 
 | 
 * particular file as subject to the "Classpath" exception as provided  | 
 | 
 * by Oracle in the LICENSE file that accompanied this code.  | 
 | 
 *  | 
 | 
 * 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 java.lang.constant;  | 
 | 
 | 
 | 
import java.lang.Enum.EnumDesc;  | 
 | 
import java.lang.invoke.MethodHandle;  | 
 | 
import java.lang.invoke.MethodHandles;  | 
 | 
import java.lang.invoke.VarHandle;  | 
 | 
import java.lang.invoke.VarHandle.VarHandleDesc;  | 
 | 
import java.util.Arrays;  | 
 | 
import java.util.List;  | 
 | 
import java.util.Map;  | 
 | 
import java.util.Objects;  | 
 | 
import java.util.function.Function;  | 
 | 
import java.util.stream.Stream;  | 
 | 
 | 
 | 
import static java.lang.constant.ConstantDescs.CD_Class;  | 
 | 
import static java.lang.constant.ConstantDescs.CD_VarHandle;  | 
 | 
import static java.lang.constant.ConstantDescs.DEFAULT_NAME;  | 
 | 
import static java.lang.constant.ConstantUtils.EMPTY_CONSTANTDESC;  | 
 | 
import static java.lang.constant.ConstantUtils.validateMemberName;  | 
 | 
import static java.util.Objects.requireNonNull;  | 
 | 
import static java.util.stream.Collectors.joining;  | 
 | 
 | 
 | 
/**  | 
 | 
 * A <a href="package-summary.html#nominal">nominal descriptor</a> for a  | 
 | 
 * dynamic constant (one described in the constant pool with  | 
 | 
 * {@code Constant_Dynamic_info}.) | 
 | 
 *  | 
 | 
 * <p>Concrete subtypes of {@linkplain DynamicConstantDesc} should be immutable | 
 | 
 * and their behavior should not rely on object identity.  | 
 | 
 *  | 
 | 
 * @param <T> the type of the dynamic constant  | 
 | 
 *  | 
 | 
 * @since 12  | 
 | 
 */  | 
 | 
public abstract non-sealed class DynamicConstantDesc<T>  | 
 | 
        implements ConstantDesc { | 
 | 
 | 
 | 
    private final DirectMethodHandleDesc bootstrapMethod;  | 
 | 
    private final ConstantDesc[] bootstrapArgs;  | 
 | 
    private final String constantName;  | 
 | 
    private final ClassDesc constantType;  | 
 | 
 | 
 | 
    /**  | 
 | 
     * Creates a nominal descriptor for a dynamic constant.  | 
 | 
     *  | 
 | 
     * @param bootstrapMethod a {@link DirectMethodHandleDesc} describing the | 
 | 
     *                        bootstrap method for the constant  | 
 | 
     * @param constantName The unqualified name that would appear in the {@code NameAndType} | 
 | 
     *                     operand of the {@code LDC} for this constant | 
 | 
     * @param constantType a {@link ClassDesc} describing the type | 
 | 
     *                     that would appear in the {@code NameAndType} operand | 
 | 
     *                     of the {@code LDC} for this constant | 
 | 
     * @param bootstrapArgs {@link ConstantDesc}s describing the static arguments | 
 | 
     *                      to the bootstrap, that would appear in the  | 
 | 
     *                      {@code BootstrapMethods} attribute | 
 | 
     * @throws NullPointerException if any argument is null  | 
 | 
     * @throws IllegalArgumentException if the {@code name} has the incorrect | 
 | 
     * format  | 
 | 
     * @jvms 4.2.2 Unqualified Names  | 
 | 
     */  | 
 | 
    protected DynamicConstantDesc(DirectMethodHandleDesc bootstrapMethod,  | 
 | 
                                  String constantName,  | 
 | 
                                  ClassDesc constantType,  | 
 | 
                                  ConstantDesc... bootstrapArgs) { | 
 | 
        this.bootstrapMethod = requireNonNull(bootstrapMethod);  | 
 | 
        this.constantName = validateMemberName(requireNonNull(constantName), true);  | 
 | 
        this.constantType = requireNonNull(constantType);  | 
 | 
        this.bootstrapArgs = requireNonNull(bootstrapArgs).clone();  | 
 | 
 | 
 | 
        if (constantName.length() == 0)  | 
 | 
            throw new IllegalArgumentException("Illegal invocation name: " + constantName); | 
 | 
    }  | 
 | 
 | 
 | 
    /**  | 
 | 
     * Returns a nominal descriptor for a dynamic constant, transforming it into  | 
 | 
     * a more specific type if the constant bootstrap is a well-known one and a  | 
 | 
     * more specific nominal descriptor type (e.g., ClassDesc) is available.  | 
 | 
     *  | 
 | 
     * <p>Classes whose {@link Constable#describeConstable()} method produce | 
 | 
     * a {@linkplain DynamicConstantDesc} with a well-known bootstrap including | 
 | 
     * {@link Class} (for instances describing primitive types), {@link Enum}, | 
 | 
     * and {@link VarHandle}. | 
 | 
     *  | 
 | 
     * <p>Bytecode-reading APIs that process the constant pool and wish to expose  | 
 | 
     * entries as {@link ConstantDesc} to their callers should generally use this | 
 | 
     * method in preference to {@link #ofNamed(DirectMethodHandleDesc, String, ClassDesc, ConstantDesc...)} | 
 | 
     * because this may result in a more specific type that can be provided to  | 
 | 
     * callers.  | 
 | 
     *  | 
 | 
     * @param <T> the type of the dynamic constant  | 
 | 
     * @param bootstrapMethod a {@link DirectMethodHandleDesc} describing the | 
 | 
     *                        bootstrap method for the constant  | 
 | 
     * @param constantName The unqualified name that would appear in the {@code NameAndType} | 
 | 
     *                     operand of the {@code LDC} for this constant | 
 | 
     * @param constantType a {@link ClassDesc} describing the type | 
 | 
     *                     that would appear in the {@code NameAndType} operand | 
 | 
     *                     of the {@code LDC} for this constant | 
 | 
     * @param bootstrapArgs {@link ConstantDesc}s describing the static arguments | 
 | 
     *                      to the bootstrap, that would appear in the  | 
 | 
     *                      {@code BootstrapMethods} attribute | 
 | 
     * @return the nominal descriptor  | 
 | 
     * @throws NullPointerException if any argument is null  | 
 | 
     * @throws IllegalArgumentException if the {@code name} has the incorrect | 
 | 
     * format  | 
 | 
     * @jvms 4.2.2 Unqualified Names  | 
 | 
     */  | 
 | 
    // Do not call this method from the static initialization of java.lang.constant.ConstantDescs  | 
 | 
    // since that can lead to potential deadlock during multi-threaded concurrent execution  | 
 | 
    public static<T> ConstantDesc ofCanonical(DirectMethodHandleDesc bootstrapMethod,  | 
 | 
                                              String constantName,  | 
 | 
                                              ClassDesc constantType,  | 
 | 
                                              ConstantDesc[] bootstrapArgs) { | 
 | 
        return DynamicConstantDesc.<T>ofNamed(bootstrapMethod, constantName, constantType, bootstrapArgs)  | 
 | 
                .tryCanonicalize();  | 
 | 
    }  | 
 | 
 | 
 | 
    /**  | 
 | 
     * Returns a nominal descriptor for a dynamic constant.  | 
 | 
     *  | 
 | 
     * @param <T> the type of the dynamic constant  | 
 | 
     * @param bootstrapMethod a {@link DirectMethodHandleDesc} describing the | 
 | 
     *                        bootstrap method for the constant  | 
 | 
     * @param constantName The unqualified name that would appear in the {@code NameAndType} | 
 | 
     *                     operand of the {@code LDC} for this constant | 
 | 
     * @param constantType a {@link ClassDesc} describing the type | 
 | 
     *                     that would appear in the {@code NameAndType} operand | 
 | 
     *                     of the {@code LDC} for this constant | 
 | 
     * @param bootstrapArgs {@link ConstantDesc}s describing the static arguments | 
 | 
     *                      to the bootstrap, that would appear in the  | 
 | 
     *                      {@code BootstrapMethods} attribute | 
 | 
     * @return the nominal descriptor  | 
 | 
     * @throws NullPointerException if any argument is null  | 
 | 
     * @throws IllegalArgumentException if the {@code name} has the incorrect | 
 | 
     * format  | 
 | 
     * @jvms 4.2.2 Unqualified Names  | 
 | 
     */  | 
 | 
 | 
 | 
    public static<T> DynamicConstantDesc<T> ofNamed(DirectMethodHandleDesc bootstrapMethod,  | 
 | 
                                                    String constantName,  | 
 | 
                                                    ClassDesc constantType,  | 
 | 
                                                    ConstantDesc... bootstrapArgs) { | 
 | 
        return new AnonymousDynamicConstantDesc<>(bootstrapMethod, constantName, constantType, bootstrapArgs);  | 
 | 
    }  | 
 | 
 | 
 | 
    /**  | 
 | 
     * Returns a nominal descriptor for a dynamic constant whose name parameter  | 
 | 
     * is {@link ConstantDescs#DEFAULT_NAME}, and whose type parameter is always | 
 | 
     * the same as the bootstrap method return type.  | 
 | 
     *  | 
 | 
     * @param <T> the type of the dynamic constant  | 
 | 
     * @param bootstrapMethod a {@link DirectMethodHandleDesc} describing the | 
 | 
     *                        bootstrap method for the constant  | 
 | 
     * @param bootstrapArgs {@link ConstantDesc}s describing the static arguments | 
 | 
     *                      to the bootstrap, that would appear in the  | 
 | 
     *                      {@code BootstrapMethods} attribute | 
 | 
     * @return the nominal descriptor  | 
 | 
     * @throws NullPointerException if any argument is null  | 
 | 
     * @jvms 4.2.2 Unqualified Names  | 
 | 
     */  | 
 | 
    public static<T> DynamicConstantDesc<T> of(DirectMethodHandleDesc bootstrapMethod,  | 
 | 
                                               ConstantDesc... bootstrapArgs) { | 
 | 
        return ofNamed(bootstrapMethod, DEFAULT_NAME, bootstrapMethod.invocationType().returnType(), bootstrapArgs);  | 
 | 
    }  | 
 | 
 | 
 | 
    /**  | 
 | 
     * Returns a nominal descriptor for a dynamic constant whose bootstrap has  | 
 | 
     * no static arguments, whose name parameter is {@link ConstantDescs#DEFAULT_NAME}, | 
 | 
     * and whose type parameter is always the same as the bootstrap method return type.  | 
 | 
     *  | 
 | 
     * @param <T> the type of the dynamic constant  | 
 | 
     * @param bootstrapMethod a {@link DirectMethodHandleDesc} describing the | 
 | 
     *                        bootstrap method for the constant  | 
 | 
     * @return the nominal descriptor  | 
 | 
     * @throws NullPointerException if any argument is null  | 
 | 
     */  | 
 | 
    public static<T> DynamicConstantDesc<T> of(DirectMethodHandleDesc bootstrapMethod) { | 
 | 
        return of(bootstrapMethod, EMPTY_CONSTANTDESC);  | 
 | 
    }  | 
 | 
 | 
 | 
    /**  | 
 | 
     * Returns the name that would appear in the {@code NameAndType} operand | 
 | 
     * of the {@code LDC} for this constant. | 
 | 
     *  | 
 | 
     * @return the constant name  | 
 | 
     */  | 
 | 
    public String constantName() { | 
 | 
        return constantName;  | 
 | 
    }  | 
 | 
 | 
 | 
    /**  | 
 | 
     * Returns a {@link ClassDesc} describing the type that would appear in the | 
 | 
     * {@code NameAndType} operand of the {@code LDC} for this constant. | 
 | 
     *  | 
 | 
     * @return the constant type  | 
 | 
     */  | 
 | 
    public ClassDesc constantType() { | 
 | 
        return constantType;  | 
 | 
    }  | 
 | 
 | 
 | 
    /**  | 
 | 
     * Returns a {@link MethodHandleDesc} describing the bootstrap method for | 
 | 
     * this constant.  | 
 | 
     *  | 
 | 
     * @return the bootstrap method  | 
 | 
     */  | 
 | 
    public DirectMethodHandleDesc bootstrapMethod() { | 
 | 
        return bootstrapMethod;  | 
 | 
    }  | 
 | 
 | 
 | 
    /**  | 
 | 
     * Returns the bootstrap arguments for this constant.  | 
 | 
     *  | 
 | 
     * @return the bootstrap arguments  | 
 | 
     */  | 
 | 
    public ConstantDesc[] bootstrapArgs() { | 
 | 
        return bootstrapArgs.clone();  | 
 | 
    }  | 
 | 
 | 
 | 
    /**  | 
 | 
     * Returns the bootstrap arguments for this constant as an immutable {@link List}. | 
 | 
     *  | 
 | 
     * @return a {@link List} of the bootstrap arguments | 
 | 
     */  | 
 | 
    public List<ConstantDesc> bootstrapArgsList() { | 
 | 
        return List.of(bootstrapArgs);  | 
 | 
    }  | 
 | 
 | 
 | 
    @SuppressWarnings("unchecked") | 
 | 
    public T resolveConstantDesc(MethodHandles.Lookup lookup) throws ReflectiveOperationException { | 
 | 
        try { | 
 | 
            MethodHandle bsm = (MethodHandle) bootstrapMethod.resolveConstantDesc(lookup);  | 
 | 
            if (bsm.type().parameterCount() < 2 ||  | 
 | 
                !MethodHandles.Lookup.class.isAssignableFrom(bsm.type().parameterType(0))) { | 
 | 
                throw new BootstrapMethodError(  | 
 | 
                        "Invalid bootstrap method declared for resolving a dynamic constant: " + bootstrapMethod);  | 
 | 
            }  | 
 | 
            Object[] bsmArgs = new Object[3 + bootstrapArgs.length];  | 
 | 
            bsmArgs[0] = lookup;  | 
 | 
            bsmArgs[1] = constantName;  | 
 | 
            bsmArgs[2] = constantType.resolveConstantDesc(lookup);  | 
 | 
            for (int i = 0; i < bootstrapArgs.length; i++)  | 
 | 
                bsmArgs[3 + i] = bootstrapArgs[i].resolveConstantDesc(lookup);  | 
 | 
 | 
 | 
            return (T) bsm.invokeWithArguments(bsmArgs);  | 
 | 
        } catch (Error e) { | 
 | 
            throw e;  | 
 | 
        } catch (Throwable t) { | 
 | 
            throw new BootstrapMethodError(t);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private ConstantDesc tryCanonicalize() { | 
 | 
        Function<DynamicConstantDesc<?>, ConstantDesc> f = CanonicalMapHolder.CANONICAL_MAP.get(bootstrapMethod);  | 
 | 
        if (f != null) { | 
 | 
            try { | 
 | 
                return f.apply(this);  | 
 | 
            }  | 
 | 
            catch (Throwable t) { | 
 | 
                return this;  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return this;  | 
 | 
    }  | 
 | 
 | 
 | 
    private static ConstantDesc canonicalizeNull(DynamicConstantDesc<?> desc) { | 
 | 
        if (desc.bootstrapArgs.length != 0)  | 
 | 
            return desc;  | 
 | 
        return ConstantDescs.NULL;  | 
 | 
    }  | 
 | 
 | 
 | 
    private static ConstantDesc canonicalizeEnum(DynamicConstantDesc<?> desc) { | 
 | 
        if (desc.bootstrapArgs.length != 0  | 
 | 
            || desc.constantName == null)  | 
 | 
            return desc;  | 
 | 
        return EnumDesc.of(desc.constantType, desc.constantName);  | 
 | 
    }  | 
 | 
 | 
 | 
    private static ConstantDesc canonicalizePrimitiveClass(DynamicConstantDesc<?> desc) { | 
 | 
        if (desc.bootstrapArgs.length != 0  | 
 | 
            || !desc.constantType().equals(CD_Class)  | 
 | 
            || desc.constantName == null)  | 
 | 
            return desc;  | 
 | 
        return ClassDesc.ofDescriptor(desc.constantName);  | 
 | 
    }  | 
 | 
 | 
 | 
    private static ConstantDesc canonicalizeStaticFieldVarHandle(DynamicConstantDesc<?> desc) { | 
 | 
        if (desc.bootstrapArgs.length != 2  | 
 | 
                || !desc.constantType().equals(CD_VarHandle))  | 
 | 
            return desc;  | 
 | 
        return VarHandleDesc.ofStaticField((ClassDesc) desc.bootstrapArgs[0],  | 
 | 
                                     desc.constantName,  | 
 | 
                                     (ClassDesc) desc.bootstrapArgs[1]);  | 
 | 
    }  | 
 | 
 | 
 | 
    private static ConstantDesc canonicalizeFieldVarHandle(DynamicConstantDesc<?> desc) { | 
 | 
        if (desc.bootstrapArgs.length != 2  | 
 | 
            || !desc.constantType().equals(CD_VarHandle))  | 
 | 
            return desc;  | 
 | 
        return VarHandleDesc.ofField((ClassDesc) desc.bootstrapArgs[0],  | 
 | 
                                     desc.constantName,  | 
 | 
                                     (ClassDesc) desc.bootstrapArgs[1]);  | 
 | 
    }  | 
 | 
 | 
 | 
    private static ConstantDesc canonicalizeArrayVarHandle(DynamicConstantDesc<?> desc) { | 
 | 
        if (desc.bootstrapArgs.length != 1  | 
 | 
            || !desc.constantType().equals(CD_VarHandle))  | 
 | 
            return desc;  | 
 | 
        return VarHandleDesc.ofArray((ClassDesc) desc.bootstrapArgs[0]);  | 
 | 
    }  | 
 | 
 | 
 | 
    // @@@ To eventually support in canonicalization: DCR with BSM=MHR_METHODHANDLEDESC_ASTYPE becomes AsTypeMHDesc  | 
 | 
 | 
 | 
    /**  | 
 | 
     * Compares the specified object with this descriptor for equality.  Returns  | 
 | 
     * {@code true} if and only if the specified object is also a | 
 | 
     * {@linkplain DynamicConstantDesc}, and both descriptors have equal | 
 | 
     * bootstrap methods, bootstrap argument lists, constant name, and  | 
 | 
     * constant type.  | 
 | 
     *  | 
 | 
     * @param o the {@code DynamicConstantDesc} to compare to this | 
 | 
     *       {@code DynamicConstantDesc} | 
 | 
     * @return {@code true} if the specified {@code DynamicConstantDesc} | 
 | 
     *      is equal to this {@code DynamicConstantDesc}. | 
 | 
     *  | 
 | 
     */  | 
 | 
    @Override  | 
 | 
    public final boolean equals(Object o) { | 
 | 
        if (this == o) return true;  | 
 | 
        return (o instanceof DynamicConstantDesc<?> desc)  | 
 | 
                && Objects.equals(bootstrapMethod, desc.bootstrapMethod)  | 
 | 
                && Arrays.equals(bootstrapArgs, desc.bootstrapArgs)  | 
 | 
                && Objects.equals(constantName, desc.constantName)  | 
 | 
                && Objects.equals(constantType, desc.constantType);  | 
 | 
    }  | 
 | 
 | 
 | 
    @Override  | 
 | 
    public final int hashCode() { | 
 | 
        int result = Objects.hash(bootstrapMethod, constantName, constantType);  | 
 | 
        result = 31 * result + Arrays.hashCode(bootstrapArgs);  | 
 | 
        return result;  | 
 | 
    }  | 
 | 
 | 
 | 
    /**  | 
 | 
     * Returns a compact textual description of this constant description,  | 
 | 
     * including the bootstrap method, the constant name and type, and  | 
 | 
     * the static bootstrap arguments.  | 
 | 
     *  | 
 | 
     * @return A compact textual description of this call site descriptor  | 
 | 
     */  | 
 | 
    @Override  | 
 | 
    public String toString() { | 
 | 
        return String.format("DynamicConstantDesc[%s::%s(%s%s)%s]", | 
 | 
                             bootstrapMethod.owner().displayName(),  | 
 | 
                             bootstrapMethod.methodName(),  | 
 | 
                             constantName.equals(ConstantDescs.DEFAULT_NAME) ? "" : constantName + "/",  | 
 | 
                             Stream.of(bootstrapArgs).map(Object::toString).collect(joining(",")), | 
 | 
                             constantType.displayName());  | 
 | 
    }  | 
 | 
 | 
 | 
    private static class AnonymousDynamicConstantDesc<T> extends DynamicConstantDesc<T> { | 
 | 
        AnonymousDynamicConstantDesc(DirectMethodHandleDesc bootstrapMethod, String constantName, ClassDesc constantType, ConstantDesc... bootstrapArgs) { | 
 | 
            super(bootstrapMethod, constantName, constantType, bootstrapArgs);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private static final class CanonicalMapHolder { | 
 | 
        static final Map<MethodHandleDesc, Function<DynamicConstantDesc<?>, ConstantDesc>> CANONICAL_MAP =  | 
 | 
                Map.ofEntries(  | 
 | 
                    Map.entry(ConstantDescs.BSM_PRIMITIVE_CLASS, DynamicConstantDesc::canonicalizePrimitiveClass),  | 
 | 
                    Map.entry(ConstantDescs.BSM_ENUM_CONSTANT, DynamicConstantDesc::canonicalizeEnum),  | 
 | 
                    Map.entry(ConstantDescs.BSM_NULL_CONSTANT, DynamicConstantDesc::canonicalizeNull),  | 
 | 
                    Map.entry(ConstantDescs.BSM_VARHANDLE_STATIC_FIELD, DynamicConstantDesc::canonicalizeStaticFieldVarHandle),  | 
 | 
                    Map.entry(ConstantDescs.BSM_VARHANDLE_FIELD, DynamicConstantDesc::canonicalizeFieldVarHandle),  | 
 | 
                    Map.entry(ConstantDescs.BSM_VARHANDLE_ARRAY, DynamicConstantDesc::canonicalizeArrayVarHandle));  | 
 | 
    }  | 
 | 
}  |