/*
 * Copyright (c) 2022, 2025, 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.classfile.constantpool;

import java.lang.classfile.BootstrapMethodEntry;
import java.lang.classfile.ClassFile;
import java.lang.classfile.ClassFileBuilder;
import java.lang.classfile.ClassModel;
import java.lang.classfile.CodeBuilder;
import java.lang.classfile.attribute.ConstantValueAttribute;
import java.lang.classfile.instruction.ConstantInstruction;
import java.lang.constant.*;
import java.lang.invoke.MethodHandleInfo;
import java.util.List;
import java.util.function.Consumer;

import jdk.internal.classfile.impl.AbstractPoolEntry;
import jdk.internal.classfile.impl.AbstractPoolEntry.ClassEntryImpl;
import jdk.internal.classfile.impl.ClassReaderImpl;
import jdk.internal.classfile.impl.SplitConstantPool;
import jdk.internal.classfile.impl.TemporaryConstantPool;
import jdk.internal.classfile.impl.Util;

import static java.util.Objects.requireNonNull;

/**
 * Builder for the constant pool of a {@code class} file.  Provides read and
 * write access to the constant pool that is {@linkplain ClassFileBuilder#constantPool()
 * being built}.  Writing is append-only (the index of new entries monotonically
 * increase) and idempotent (entry-bearing methods will return an existing entry
 * if there is a suitable one).
 * <p>
 * For {@code class} file building, an overload of {@link ClassFile#build(
 * ClassEntry, ConstantPoolBuilder, Consumer) ClassFile::build} takes a
 * {@code ConstantPoolBuilder}.  For {@code class} file transformations via
 * {@link ClassFile#transformClass ClassFile::transformClass}, the {@link
 * ClassFile.ConstantPoolSharingOption} controls how the constant pool builder
 * of the resulting {@code class} is created.
 *
 * <h2 id="alien">Alien Constant Pool Entries</h2>
 * In {@code class} file building and constant pool building, some constant pool
 * entries supplied may be {@linkplain #canWriteDirect(ConstantPool) alien}
 * to this constant pool builder of the active class file builder.  For example,
 * {@link #classEntry(Utf8Entry) classEntry(Utf8Entry)} may be called with an
 * alien UTF8 entry.  Alien entries will be converted to a pool entry in
 * this constant pool builder, reusing equivalent entries or adding new entries
 * if there is none.  As a result, all pool entries returned by entry-bearing
 * methods in this constant pool builder belong to this constant pool.
 * <p>
 * Some {@link ClassFileBuilder} methods may have their outputs adjusted if they
 * receive pool entries alien to {@linkplain ClassFileBuilder#constantPool
 * their constant pools}.  For example, if an {@link ConstantInstruction#ofLoad
 * ldc_w} instruction with an alien entry is written to a {@link CodeBuilder},
 * the {@code CodeBuilder} may emit a functionally equivalent {@code ldc}
 * instruction instead, if the converted entry can be encoded in such an
 * instruction.
 * <p>
 * To avoid the conversion of alien constant pool entries, such as for the
 * accuracy of the generated {@code class} file, users can always supply
 * constant pool entries obtained by calling the constant pool builder
 * entry-bearing methods of the constant pools associated with the {@code
 * ClassFileBuilder}.  Otherwise, the conversions have no impact on the
 * behaviors of the generated {@code class} files.
 *
 * @see ClassFileBuilder#constantPool()
 * @since 24
 */
public sealed interface ConstantPoolBuilder
        extends ConstantPool
        permits SplitConstantPool, TemporaryConstantPool {

    /**
     * {@return a new constant pool builder}  The new constant pool builder will
     * be pre-populated with the contents of the constant pool {@linkplain
     * ClassModel#constantPool() associated with} the given class model.  The
     * index of new entries will start from the {@link ConstantPool#size()
     * size()} of the source pool.
     *
     * @param classModel the class to copy from
     * @see ClassFile#build(ClassEntry, ConstantPoolBuilder, Consumer)
     * @see ClassFile.ConstantPoolSharingOption#SHARED_POOL
     */
    static ConstantPoolBuilder of(ClassModel classModel) {
        return new SplitConstantPool((ClassReaderImpl) classModel.constantPool());
    }

    /**
     * {@return a new constant pool builder}  The new constant pool builder
     * will be empty.  The index of new entries will start from {@code 1}.
     *
     * @see ClassFile.ConstantPoolSharingOption#NEW_POOL
     */
    static ConstantPoolBuilder of() {
        return new SplitConstantPool();
    }

    /**
     * {@return {@code true} if the index of any entry in the given constant
     * pool refers to the same entry in this builder}  This may be because they
     * are the same builder, or because this builder was {@linkplain
     * #of(ClassModel) pre-populated} from the given constant pool.
     * <p>
     * If the constant pool of an entry is not directly writable to this pool,
     * it is alien to this pool, and a {@link ClassFileBuilder} associated
     * with this constant pool will convert that alien constant pool entry.
     *
     * @param constantPool the given constant pool
     * @see ClassFileBuilder#constantPool() ClassFileBuilder::constantPool
     * @see ##alien Alien Constant Pool Entries
     */
    boolean canWriteDirect(ConstantPool constantPool);

    /**
     * {@return a {@link Utf8Entry} describing the provided {@link String}}
     *
     * @param s the string
     * @see Utf8Entry#stringValue() Utf8Entry::stringValue
     */
    Utf8Entry utf8Entry(String s);

    /**
     * {@return a {@link Utf8Entry} describing the {@linkplain
     * ClassDesc#descriptorString() field descriptor string} of the provided
     * {@link ClassDesc}}
     *
     * @apiNote
     * The resulting {@code Utf8Entry} is usually not {@linkplain
     * #classEntry(Utf8Entry) referable by} a {@link ClassEntry}, which uses
     * internal form of binary names.
     *
     * @param desc the symbolic descriptor for the class
     */
    default Utf8Entry utf8Entry(ClassDesc desc) {
        return utf8Entry(desc.descriptorString());
    }

    /**
     * {@return a {@link Utf8Entry} describing the {@linkplain
     * MethodTypeDesc#descriptorString() method descriptor string} of the
     * provided {@link MethodTypeDesc}}
     *
     * @param desc the symbolic descriptor for the method type
     */
    default Utf8Entry utf8Entry(MethodTypeDesc desc) {
        return utf8Entry(desc.descriptorString());
    }

    /**
     * {@return a {@link ClassEntry} referring to the provided {@link
     * Utf8Entry}}  The {@code Utf8Entry} describes the internal form
     * of the binary name of a class or interface or the field descriptor
     * string of an array type.
     *
     * @param ne the {@code Utf8Entry}
     * @see ClassEntry#name() ClassEntry::name
     */
    ClassEntry classEntry(Utf8Entry ne);

    /**
     * {@return a {@link ClassEntry} describing the same reference type
     * as the provided {@link ClassDesc}}
     *
     * @param classDesc the symbolic descriptor for the reference type
     * @throws IllegalArgumentException if {@code classDesc} represents a
     *         primitive type
     * @see ClassEntry#asSymbol() ClassEntry::asSymbol
     */
    default ClassEntry classEntry(ClassDesc classDesc) {
        if (requireNonNull(classDesc).isPrimitive()) {
            throw new IllegalArgumentException("Cannot be encoded as ClassEntry: " + classDesc.displayName());
        }
        ClassEntryImpl ret = (ClassEntryImpl)classEntry(utf8Entry(classDesc.isArray() ? classDesc.descriptorString() : Util.toInternalName(classDesc)));
        ret.sym = classDesc;
        return ret;
    }

    /**
     * {@return a {@link PackageEntry} referring to the provided {@link
     * Utf8Entry}}  The {@code Utf8Entry} describes the internal form
     * of the name of a package.
     *
     * @param nameEntry the {@code Utf8Entry}
     * @see PackageEntry#name() PackageEntry::name
     */
    PackageEntry packageEntry(Utf8Entry nameEntry);

    /**
     * {@return a {@link PackageEntry} describing the same package as the
     * provided {@link PackageDesc}}
     *
     * @param packageDesc the symbolic descriptor for the package
     * @see PackageEntry#asSymbol() PackageEntry::asSymbol
     */
    default PackageEntry packageEntry(PackageDesc packageDesc) {
        return packageEntry(utf8Entry(packageDesc.internalName()));
    }

    /**
     * {@return a {@link ModuleEntry} referring to the provided {@link
     * Utf8Entry}}  The {@code Utf8Entry} describes the module name.
     *
     * @param moduleName the constant pool entry describing the module name
     * @see ModuleEntry#name() ModuleEntry::name
     */
    ModuleEntry moduleEntry(Utf8Entry moduleName);

    /**
     * {@return a {@link ModuleEntry} describing the same module as the provided
     * {@link ModuleDesc}}
     *
     * @param moduleDesc the symbolic descriptor for the module
     * @see ModuleEntry#asSymbol() ModuleEntry::asSymbol
     */
    default ModuleEntry moduleEntry(ModuleDesc moduleDesc) {
        return moduleEntry(utf8Entry(moduleDesc.name()));
    }

    /**
     * {@return a {@link NameAndTypeEntry} referring to the provided name and
     * type {@link Utf8Entry}}  The name {@code Utf8Entry} describes an
     * unqualified name or the special name {@value ConstantDescs#INIT_NAME},
     * and the type {@code Utf8Entry} describes a field or method descriptor
     * string.
     *
     * @param nameEntry the name {@code Utf8Entry}
     * @param typeEntry the type {@code Utf8Entry}
     * @see NameAndTypeEntry#name() NameAndTypeEntry::name
     * @see NameAndTypeEntry#type() NameAndTypeEntry::type
     */
    NameAndTypeEntry nameAndTypeEntry(Utf8Entry nameEntry, Utf8Entry typeEntry);

    /**
     * {@return a {@link NameAndTypeEntry} describing the provided unqualified
     * name and field descriptor}
     *
     * @param name the unqualified name
     * @param type the field descriptor
     */
    default NameAndTypeEntry nameAndTypeEntry(String name, ClassDesc type) {
        return nameAndTypeEntry(utf8Entry(name), utf8Entry(type));
    }

    /**
     * {@return a {@link NameAndTypeEntry} describing the provided name and
     * method descriptor}  The name can be an unqualified name or the
     * special name {@value ConstantDescs#INIT_NAME}.
     *
     * @param name the unqualified name, or {@value ConstantDescs#INIT_NAME}
     * @param type the method descriptor
     */
    default NameAndTypeEntry nameAndTypeEntry(String name, MethodTypeDesc type) {
        return nameAndTypeEntry(utf8Entry(name), utf8Entry(type));
    }

    /**
     * {@return a {@link FieldRefEntry} referring to a {@link ClassEntry} and a
     * {@link NameAndTypeEntry}}  The {@code ClassEntry} describes a class or
     * interface that has this field as a member, and the {@code
     * NameAndTypeEntry} describes the unqualified name and the field descriptor
     * for this field.
     *
     * @param owner the {@code ClassEntry}
     * @param nameAndType the {@code NameAndTypeEntry}
     * @see FieldRefEntry#owner() FieldRefEntry::owner
     * @see FieldRefEntry#nameAndType() FieldRefEntry::nameAndType
     */
    FieldRefEntry fieldRefEntry(ClassEntry owner, NameAndTypeEntry nameAndType);

    /**
     * {@return a {@link FieldRefEntry} describing a field of a class}
     *
     * @param owner the class or interface the field is a member of
     * @param name the unqualified name of the field
     * @param type the field descriptor
     * @throws IllegalArgumentException if {@code owner} represents a primitive type
     * @see FieldRefEntry#typeSymbol() FieldRefEntry::typeSymbol
     */
    default FieldRefEntry fieldRefEntry(ClassDesc owner, String name, ClassDesc type) {
        return fieldRefEntry(classEntry(owner), nameAndTypeEntry(name, type));
    }

    /**
     * {@return a {@link MethodRefEntry} referring to a {@link ClassEntry} and a
     * {@link NameAndTypeEntry}}  The {@code ClassEntry} describes a class that
     * has this method as a member, and the {@code NameAndTypeEntry} describes
     * the unqualified name or the special name {@value ConstantDescs#INIT_NAME}
     * and the method descriptor for this method.
     *
     * @param owner the {@code ClassEntry}
     * @param nameAndType the {@code NameAndTypeEntry}
     * @see MethodRefEntry#owner() MethodRefEntry::owner
     * @see MethodRefEntry#nameAndType() MethodRefEntry::nameAndType
     */
    MethodRefEntry methodRefEntry(ClassEntry owner, NameAndTypeEntry nameAndType);

    /**
     * {@return a {@link MethodRefEntry} describing a method of a class}
     *
     * @param owner the class the method is a member of
     * @param name the unqualified name, or special name {@value
     *        ConstantDescs#INIT_NAME}, of the method
     * @param type the method descriptor
     * @throws IllegalArgumentException if {@code owner} represents a primitive type
     * @see MethodRefEntry#typeSymbol() MethodRefEntry::typeSymbol
     */
    default MethodRefEntry methodRefEntry(ClassDesc owner, String name, MethodTypeDesc type) {
        return methodRefEntry(classEntry(owner), nameAndTypeEntry(name, type));
    }

    /**
     * {@return an {@link InterfaceMethodRefEntry} referring to a {@link
     * ClassEntry} and a {@link NameAndTypeEntry}}  The {@code ClassEntry}
     * describes an interface that has this method as a member, and the {@code
     * NameAndTypeEntry} describes the unqualified name and the method
     * descriptor for this method.
     *
     * @param owner the {@code ClassEntry}
     * @param nameAndType the {@code NameAndTypeEntry}
     * @see InterfaceMethodRefEntry#owner() InterfaceMethodRefEntry::owner
     * @see InterfaceMethodRefEntry#nameAndType()
     *      InterfaceMethodRefEntry::nameAndType
     */
    InterfaceMethodRefEntry interfaceMethodRefEntry(ClassEntry owner, NameAndTypeEntry nameAndType);

    /**
     * {@return an {@link InterfaceMethodRefEntry} describing a method of an
     * interface}
     *
     * @param owner the interface the method is a member of
     * @param name the unqualified name of the method
     * @param type the method descriptor
     * @throws IllegalArgumentException if {@code owner} represents a primitive type
     * @see InterfaceMethodRefEntry#typeSymbol() InterfaceMethodRefEntry::typeSymbol
     */
    default InterfaceMethodRefEntry interfaceMethodRefEntry(ClassDesc owner, String name, MethodTypeDesc type) {
        return interfaceMethodRefEntry(classEntry(owner), nameAndTypeEntry(name, type));
    }

    /**
     * {@return a {@link MethodTypeEntry} describing the same method type as
     * the provided {@link MethodTypeDesc}}
     *
     * @param descriptor the symbolic descriptor of the method type
     * @see MethodTypeEntry#asSymbol() MethodTypeEntry::asSymbol
     */
    MethodTypeEntry methodTypeEntry(MethodTypeDesc descriptor);

    /**
     * {@return a {@link MethodTypeEntry} referring to a {@link Utf8Entry}}
     * The {@code Utf8Entry} represents a method descriptor string.
     *
     * @param descriptor the {@code Utf8Entry}
     * @see MethodTypeEntry#descriptor() MethodTypeEntry::descriptor
     */
    MethodTypeEntry methodTypeEntry(Utf8Entry descriptor);

    /**
     * {@return a {@link MethodHandleEntry} describing the same method handle as
     * the given {@link DirectMethodHandleDesc}}
     *
     * @param descriptor the symbolic descriptor of the method handle
     * @see MethodHandleEntry#asSymbol() MethodHandleEntry::asSymbol
     */
    default MethodHandleEntry methodHandleEntry(DirectMethodHandleDesc descriptor) {
        var owner = classEntry(descriptor.owner());
        var nat = nameAndTypeEntry(utf8Entry(descriptor.methodName()), utf8Entry(descriptor.lookupDescriptor()));
        var ret = methodHandleEntry(descriptor.refKind(), switch (descriptor.kind()) {
            case GETTER, SETTER, STATIC_GETTER, STATIC_SETTER -> fieldRefEntry(owner, nat);
            case INTERFACE_STATIC, INTERFACE_VIRTUAL, INTERFACE_SPECIAL -> interfaceMethodRefEntry(owner, nat);
            case STATIC, VIRTUAL, SPECIAL, CONSTRUCTOR -> methodRefEntry(owner, nat);
        });
        ((AbstractPoolEntry.MethodHandleEntryImpl) ret).sym = descriptor;
        return ret;
    }

    /**
     * {@return a {@link MethodHandleEntry} encoding a reference kind and
     * referring to a {@link MemberRefEntry}}  The reference kind must be
     * in {@code [1, 9]}, and the {@code MemberRefEntry} is subject to
     * various restrictions based on the reference kind (JVMS {@jvms 4.4.8}).
     *
     * @param refKind the reference kind of the method handle
     * @param reference the {@code MemberRefEntry}
     * @see MethodHandleInfo##refkinds Reference kinds
     * @see MethodHandleEntry#kind() MethodHandleEntry::kind
     * @see MethodHandleEntry#reference() MethodHandleEntry::reference
     */
    MethodHandleEntry methodHandleEntry(int refKind, MemberRefEntry reference);

    /**
     * {@return an {@link InvokeDynamicEntry} describing the same dynamic call
     * site as the provided {@link DynamicCallSiteDesc}}
     *
     * @param dcsd the symbolic descriptor of the dynamic call site
     * @see InvokeDynamicEntry#asSymbol() InvokeDynamicEntry::asSymbol
     */
    default InvokeDynamicEntry invokeDynamicEntry(DynamicCallSiteDesc dcsd) {
        var ret = invokeDynamicEntry(bsmEntry((DirectMethodHandleDesc)dcsd.bootstrapMethod(), List.of(dcsd.bootstrapArgs())), nameAndTypeEntry(dcsd.invocationName(), dcsd.invocationType()));
        ((AbstractPoolEntry.InvokeDynamicEntryImpl) ret).sym = dcsd;
        return ret;
    }

    /**
     * {@return an {@link InvokeDynamicEntry} referring to a {@link
     * BootstrapMethodEntry} and a {@link NameAndTypeEntry}}
     * The {@code BootstrapMethodEntry} describes the bootstrap method
     * and its invocation arguments in addition to the name and type,
     * and the {@code NameAndTypeEntry} a name and a method descriptor.
     *
     * @param bootstrapMethodEntry the {@code BootstrapMethodEntry}
     * @param nameAndType the {@code NameAndTypeEntry}
     * @see InvokeDynamicEntry#bootstrap() InvokeDynamicEntry::bootstrap
     * @see InvokeDynamicEntry#nameAndType() InvokeDynamicEntry::nameAndType
     */
    InvokeDynamicEntry invokeDynamicEntry(BootstrapMethodEntry bootstrapMethodEntry,
                                          NameAndTypeEntry nameAndType);

    /**
     * {@return a {@link ConstantDynamicEntry} describing the dynamic constant
     * as the provided {@link DynamicConstantDesc}}
     *
     * @param dcd the symbolic descriptor of the constant
     * @see ConstantDynamicEntry#asSymbol() ConstantDynamicEntry::asSymbol
     */
    default ConstantDynamicEntry constantDynamicEntry(DynamicConstantDesc<?> dcd) {
        var ret = constantDynamicEntry(bsmEntry(dcd.bootstrapMethod(), List.of(dcd.bootstrapArgs())), nameAndTypeEntry(dcd.constantName(), dcd.constantType()));
        ((AbstractPoolEntry.ConstantDynamicEntryImpl) ret).sym = dcd;
        return ret;
    }

    /**
     * {@return a {@link ConstantDynamicEntry} referring to a {@link
     * BootstrapMethodEntry} and a {@link NameAndTypeEntry}}
     * The {@code BootstrapMethodEntry} describes the bootstrap method
     * and its invocation arguments in addition to the name and type,
     * and the {@code NameAndTypeEntry} a name and a field descriptor.
     *
     * @param bootstrapMethodEntry the {@code BootstrapMethodEntry}
     * @param nameAndType the {@code NameAndTypeEntry}
     * @see ConstantDynamicEntry#bootstrap() ConstantDynamicEntry::bootstrap
     * @see ConstantDynamicEntry#nameAndType() ConstantDynamicEntry::nameAndType
     */
    ConstantDynamicEntry constantDynamicEntry(BootstrapMethodEntry bootstrapMethodEntry, NameAndTypeEntry nameAndType);

    /**
     * {@return an {@link IntegerEntry} describing the provided value}
     *
     * @param value the value
     * @see IntegerEntry#intValue() IntegerEntry::intValue
     */
    IntegerEntry intEntry(int value);

    /**
     * {@return a {@link FloatEntry} describing the provided value}
     * <p>
     * All NaN values of the {@code float} may or may not be collapsed into a
     * single {@linkplain Float#NaN "canonical" NaN value}.
     *
     * @param value the value
     * @see FloatEntry#floatValue() FloatEntry::floatValue
     */
    FloatEntry floatEntry(float value);

    /**
     * {@return a {@link LongEntry} describing the provided value}
     *
     * @param value the value
     * @see LongEntry#longValue() LongEntry::longValue
     */
    LongEntry longEntry(long value);

    /**
     * {@return a {@link DoubleEntry} describing the provided value}
     * <p>
     * All NaN values of the {@code double} may or may not be collapsed into a
     * single {@linkplain Double#NaN "canonical" NaN value}.
     *
     * @param value the value
     * @see DoubleEntry#doubleValue() DoubleEntry::doubleValue
     */
    DoubleEntry doubleEntry(double value);

    /**
     * {@return a {@link StringEntry} referring to a {@link Utf8Entry}}
     * The {@code Utf8Entry} describes the string value.
     *
     * @param utf8 the {@code Utf8Entry}
     * @see StringEntry#utf8() StringEntry::utf8
     */
    StringEntry stringEntry(Utf8Entry utf8);

    /**
     * {@return a {@link StringEntry} describing the provided value}
     *
     * @param value the value
     * @see StringEntry#stringValue() StringEntry::stringValue
     */
    default StringEntry stringEntry(String value) {
        return stringEntry(utf8Entry(value));
    }

    /**
     * {@return a {@link ConstantValueEntry} describing the provided constant
     * {@link Integer}, {@link Long}, {@link Float}, {@link Double}, or {@link
     * String} value}
     *
     * @param c the provided constant value
     * @throws IllegalArgumentException if the value is not one of {@code
     *         Integer}, {@code Long}, {@code Float}, {@code Double}, or {@code
     *         String}
     * @see ConstantValueEntry#constantValue() ConstantValueEntry::constantValue
     * @see ConstantValueAttribute#of(ConstantDesc)
     *      ConstantValueAttribute::of(ConstantDesc)
     */
    default ConstantValueEntry constantValueEntry(ConstantDesc c) {
        if (c instanceof Integer i) return intEntry(i);
        if (c instanceof String s) return stringEntry(s);
        if (c instanceof Long l) return longEntry(l);
        if (c instanceof Float f) return floatEntry(f);
        if (c instanceof Double d) return doubleEntry(d);
        throw new IllegalArgumentException("Illegal type: " + (c == null ? null : c.getClass()));
    }

    /**
     * {@return a {@link LoadableConstantEntry} describing the provided constant
     * value}
     *
     * @param c the nominal descriptor for the constant
     */
    default LoadableConstantEntry loadableConstantEntry(ConstantDesc c) {
        if (c instanceof Integer i) return intEntry(i);
        if (c instanceof String s) return stringEntry(s);
        if (c instanceof Long l) return longEntry(l);
        if (c instanceof Float f) return floatEntry(f);
        if (c instanceof Double d) return doubleEntry(d);
        if (c instanceof ClassDesc cd && !cd.isPrimitive()) return classEntry(cd);
        if (c instanceof MethodTypeDesc mtd) return methodTypeEntry(mtd);
        if (c instanceof DirectMethodHandleDesc dmhd) return methodHandleEntry(dmhd);
        if (c instanceof DynamicConstantDesc<?> dcd) return constantDynamicEntry(dcd);
        throw new IllegalArgumentException("Illegal type: " + (c == null ? null : c.getClass()));
    }

    /**
     * {@return a {@link BootstrapMethodEntry} describing the provided
     * bootstrap method and arguments}
     *
     * @param methodReference the bootstrap method
     * @param arguments the arguments
     */
    default BootstrapMethodEntry bsmEntry(DirectMethodHandleDesc methodReference,
                                          List<ConstantDesc> arguments) {
        return bsmEntry(methodHandleEntry(methodReference),
                arguments.stream().map(this::loadableConstantEntry).toList());
    }

    /**
     * {@return a {@link BootstrapMethodEntry} referring to a {@link
     * MethodHandleEntry} and a list of {@link LoadableConstantEntry}}
     * The {@code MethodHandleEntry} is the bootstrap method, and the
     * list of {@code LoadableConstantEntry} is the arguments.
     *
     * @param methodReference the {@code MethodHandleEntry}
     * @param arguments the list of {@code LoadableConstantEntry}
     * @see BootstrapMethodEntry#bootstrapMethod()
     *      BootstrapMethodEntry::bootstrapMethod
     * @see BootstrapMethodEntry#arguments() BootstrapMethodEntry::arguments
     */
    BootstrapMethodEntry bsmEntry(MethodHandleEntry methodReference,
                                  List<LoadableConstantEntry> arguments);
}
