All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.dmlloyd.classfile.impl.SplitConstantPool Maven / Gradle / Ivy

There is a newer version: 24.cr2
Show newest version
/*
 * Copyright (c) 2022, 2024, 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 io.github.dmlloyd.classfile.impl;

import io.github.dmlloyd.classfile.Attributes;
import io.github.dmlloyd.classfile.BootstrapMethodEntry;
import io.github.dmlloyd.classfile.ClassReader;
import io.github.dmlloyd.classfile.attribute.BootstrapMethodsAttribute;
import io.github.dmlloyd.classfile.constantpool.*;
import java.lang.constant.ClassDesc;
import java.lang.constant.MethodTypeDesc;
import java.util.Arrays;
import java.util.List;

import static io.github.dmlloyd.classfile.constantpool.PoolEntry.*;
import static java.util.Objects.requireNonNull;

public final class SplitConstantPool implements ConstantPoolBuilder {

    private final ClassReaderImpl parent;
    private final int parentSize, parentBsmSize;

    private int size, bsmSize;
    private PoolEntry[] myEntries;
    private BootstrapMethodEntryImpl[] myBsmEntries;
    private boolean doneFullScan;
    private EntryMap map;
    private EntryMap bsmMap;

    public SplitConstantPool() {
        this.size = 1;
        this.bsmSize = 0;
        this.myEntries = new PoolEntry[1024];
        this.myBsmEntries = new BootstrapMethodEntryImpl[8];
        this.parent = null;
        this.parentSize = 0;
        this.parentBsmSize = 0;
        this.doneFullScan = true;
    }

    public SplitConstantPool(ClassReader parent) {
        this.parent = (ClassReaderImpl) parent;
        this.parentSize = parent.size();
        this.parentBsmSize = parent.bootstrapMethodCount();
        this.size = parentSize;
        this.bsmSize = parentBsmSize;
        this.myEntries = new PoolEntry[8];
        this.myBsmEntries = new BootstrapMethodEntryImpl[8];
        this.doneFullScan = true;
    }

    @Override
    public int size() {
        return size;
    }

    @Override
    public int bootstrapMethodCount() {
        return bsmSize;
    }

    @Override
    public PoolEntry entryByIndex(int index) {
        if (index <= 0 || index >= size()) {
            throw badCP(index);
        }
        PoolEntry pe = (index < parentSize)
               ? parent.entryByIndex(index)
               : myEntries[index - parentSize];
        if (pe == null) {
            throw unusableCP(index);
        }
        return pe;
    }

    private static ConstantPoolException badCP(int index) {
        return new ConstantPoolException("Bad CP index: " + index);
    }

    private static ConstantPoolException unusableCP(int index) {
        return new ConstantPoolException("Unusable CP index: " + index);
    }

    @Override
    public  T entryByIndex(int index, Class cls) {
        return ClassReaderImpl.checkType(entryByIndex(index), index, cls);
    }

    @Override
    public BootstrapMethodEntryImpl bootstrapMethodEntry(int index) {
        if (index < 0 || index >= bootstrapMethodCount()) {
            throw new ConstantPoolException("Bad BSM index: " + index);
        }
        return (index < parentBsmSize)
               ? parent.bootstrapMethodEntry(index)
               : myBsmEntries[index - parentBsmSize];
    }

    @Override
    public boolean canWriteDirect(ConstantPool other) {
        requireNonNull(other);
        return this == other || parent == other;
    }

    public boolean writeBootstrapMethods(BufWriterImpl buf) {
        if (bsmSize == 0)
            return false;
        int pos = buf.size();
        if (parent != null && parentBsmSize != 0) {
            parent.writeBootstrapMethods(buf);
            for (int i = parentBsmSize; i < bsmSize; i++)
                bootstrapMethodEntry(i).writeTo(buf);
            int attrLen = buf.size() - pos;
            buf.patchInt(pos + 2, attrLen - 6);
            buf.patchU2(pos + 6, bsmSize);
        }
        else {
            UnboundAttribute a
                    = new UnboundAttribute.AdHocAttribute<>(Attributes.bootstrapMethods()) {

                @Override
                public void writeBody(BufWriterImpl b) {
                    buf.writeU2(bsmSize);
                    for (int i = 0; i < bsmSize; i++)
                        bootstrapMethodEntry(i).writeTo(buf);
                }

                @Override
                public Utf8Entry attributeName() {
                    return utf8Entry(Attributes.NAME_BOOTSTRAP_METHODS);
                }
            };
            a.writeTo(buf);
        }
        return true;
    }

    void writeTo(BufWriterImpl buf) {
        int writeFrom = 1;
        if (size() >= 65536) {
            throw new IllegalArgumentException(String.format("Constant pool is too large %d", size()));
        }
        buf.writeU2(size());
        if (parent != null && buf.constantPool().canWriteDirect(this)) {
            parent.writeConstantPoolEntries(buf);
            writeFrom = parent.size();
        }
        for (int i = writeFrom; i < size(); ) {
            var info = (AbstractPoolEntry) entryByIndex(i);
            info.writeTo(buf);
            i += info.width();
        }
    }

    private EntryMap map() {
        int parentSize = this.parentSize;
        var map = this.map;
        if (map == null) {
            this.map = map = new EntryMap(Math.max(size, 1024), .75f);

            // Doing a full scan here yields fall-off-the-cliff performance results,
            // especially if we only need a few entries that are already
            // inflated (such as attribute names).
            // So we inflate the map with whatever we've got from the parent, and
            // later, if we miss, we do a one-time full inflation before creating
            // a new entry.
            for (int i=1; i E internalAdd(E cpi) {
        return internalAdd(cpi, cpi.hashCode());
    }

    private  E internalAdd(E cpi, int hash) {
        int newIndex = size-parentSize;
        if (newIndex + 2 > myEntries.length) {
            myEntries = Arrays.copyOf(myEntries, 2 * newIndex, PoolEntry[].class);
        }
        myEntries[newIndex] = cpi;
        size += cpi.width();
        map().put(hash, cpi.index());
        return cpi;
    }

    private BootstrapMethodEntryImpl internalAdd(BootstrapMethodEntryImpl bsm, int hash) {
        int newIndex = bsmSize-parentBsmSize;
        if (newIndex + 2 > myBsmEntries.length) {
            myBsmEntries = Arrays.copyOf(myBsmEntries, 2 * newIndex, BootstrapMethodEntryImpl[].class);
        }
        myBsmEntries[newIndex] = bsm;
        bsmSize += 1;
        bsmMap().put(hash, bsm.index);
        return bsm;
    }

    private IntegerEntry findIntEntry(int val) {
        int hash = AbstractPoolEntry.hash1(TAG_INTEGER, Integer.hashCode(val));
        EntryMap map = map();
        for (int token = map.firstToken(hash); token != -1; token = map.nextToken(hash, token)) {
            PoolEntry e = entryByIndex(map.getIndexByToken(token));
            if (e.tag() == TAG_INTEGER
                    && e instanceof AbstractPoolEntry.IntegerEntryImpl ce
                    && ce.intValue() == val)
                return ce;
        }
        if (!doneFullScan) {
            fullScan();
            return findIntEntry(val);
        }
        return null;
    }

    private LongEntry findLongEntry(long val) {
        int hash = AbstractPoolEntry.hash1(TAG_LONG, Long.hashCode(val));
        EntryMap map = map();
        for (int token = map.firstToken(hash); token != -1; token = map.nextToken(hash, token)) {
            PoolEntry e = entryByIndex(map.getIndexByToken(token));
            if (e.tag() == TAG_LONG
                    && e instanceof AbstractPoolEntry.LongEntryImpl ce
                    && ce.longValue() == val)
                return ce;
        }
        if (!doneFullScan) {
            fullScan();
            return findLongEntry(val);
        }
        return null;
    }

    private FloatEntry findFloatEntry(float val) {
        int hash = AbstractPoolEntry.hash1(TAG_FLOAT, Float.hashCode(val));
        EntryMap map = map();
        for (int token = map.firstToken(hash); token != -1; token = map.nextToken(hash, token)) {
            PoolEntry e = entryByIndex(map.getIndexByToken(token));
            if (e.tag() == TAG_FLOAT
                    && e instanceof AbstractPoolEntry.FloatEntryImpl ce
                    && ce.floatValue() == val)
                return ce;
        }
        if (!doneFullScan) {
            fullScan();
            return findFloatEntry(val);
        }
        return null;
    }

    private DoubleEntry findDoubleEntry(double val) {
        int hash = AbstractPoolEntry.hash1(TAG_DOUBLE, Double.hashCode(val));
        EntryMap map = map();
        for (int token = map.firstToken(hash); token != -1; token = map.nextToken(hash, token)) {
            PoolEntry e = entryByIndex(map.getIndexByToken(token));
            if (e.tag() == TAG_DOUBLE
                    && e instanceof AbstractPoolEntry.DoubleEntryImpl ce
                    && ce.doubleValue() == val)
                return ce;
        }
        if (!doneFullScan) {
            fullScan();
            return findDoubleEntry(val);
        }
        return null;
    }

    private AbstractPoolEntry findEntry(int tag, T ref1) {
        // invariant: canWriteDirect(ref1.constantPool())
        int hash = AbstractPoolEntry.hash1(tag, ref1.index());
        EntryMap map = map();
        for (int token = map.firstToken(hash); token != -1; token = map.nextToken(hash, token)) {
            PoolEntry e = entryByIndex(map.getIndexByToken(token));
            if (e.tag() == tag
                && e instanceof AbstractPoolEntry.AbstractRefEntry re
                && re.ref1 == ref1)
                return re;
        }
        if (!doneFullScan) {
            fullScan();
            return findEntry(tag, ref1);
        }
        return null;
    }

    private 
            AbstractPoolEntry findEntry(int tag, T ref1, U ref2) {
        // invariant: canWriteDirect(ref1.constantPool()), canWriteDirect(ref2.constantPool())
        int hash = AbstractPoolEntry.hash2(tag, ref1.index(), ref2.index());
        EntryMap map = map();
        for (int token = map.firstToken(hash); token != -1; token = map.nextToken(hash, token)) {
            PoolEntry e = entryByIndex(map.getIndexByToken(token));
            if (e.tag() == tag
                    && e instanceof AbstractPoolEntry.AbstractRefsEntry re
                    && re.ref1 == ref1
                    && re.ref2 == ref2) {
                return re;
            }
        }
        if (!doneFullScan) {
            fullScan();
            return findEntry(tag, ref1, ref2);
        }
        return null;
    }

    private AbstractPoolEntry.Utf8EntryImpl tryFindUtf8(int hash, String target) {
        EntryMap map = map();
        for (int token = map.firstToken(hash); token != -1;
             token = map.nextToken(hash, token)) {
            PoolEntry e = entryByIndex(map.getIndexByToken(token));
            if (e.tag() == TAG_UTF8
                && e instanceof AbstractPoolEntry.Utf8EntryImpl ce
                && target.equals(ce.stringValue()))
                return ce;
        }
        if (!doneFullScan) {
            fullScan();
            return tryFindUtf8(hash, target);
        }
        return null;
    }

    private AbstractPoolEntry.Utf8EntryImpl tryFindUtf8(int hash, AbstractPoolEntry.Utf8EntryImpl target) {
        EntryMap map = map();
        for (int token = map.firstToken(hash); token != -1; token = map.nextToken(hash, token)) {
            PoolEntry e = entryByIndex(map.getIndexByToken(token));
            if (e.tag() == TAG_UTF8
                && e instanceof AbstractPoolEntry.Utf8EntryImpl ce
                && target.equalsUtf8(ce))
                return ce;
        }
        if (!doneFullScan) {
            fullScan();
            return tryFindUtf8(hash, target);
        }
        return null;
    }

    private AbstractPoolEntry.ClassEntryImpl tryFindClassOrInterface(int hash, ClassDesc cd) {
        while (true) {
            EntryMap map = map();
            for (int token = map.firstToken(hash); token != -1; token = map.nextToken(hash, token)) {
                PoolEntry e = entryByIndex(map.getIndexByToken(token));
                if (e.tag() == TAG_CLASS
                        && e instanceof AbstractPoolEntry.ClassEntryImpl ce) {
                    var esym = ce.sym;

                    if (esym != null) {
                        if (cd.equals(esym)) {
                            return ce; // definite match
                        }
                        continue; // definite mismatch
                    }

                    // no symbol available
                    if (ce.ref1.equalsString(Util.toInternalName(cd))) {
                        // definite match, propagate symbol
                        ce.sym = cd;
                        return ce;
                    }
                    // definite mismatch
                }
            }
            if (!doneFullScan) {
                fullScan();
                continue;
            }
            return null;
        }
    }

    private AbstractPoolEntry.ClassEntryImpl classEntryForClassOrInterface(ClassDesc cd) {
        var desc = cd.descriptorString();

        int hash = AbstractPoolEntry.hashClassFromDescriptor(desc.hashCode());
        var ce = tryFindClassOrInterface(hash, cd);
        if (ce != null)
            return ce;

        String internalName = Util.toInternalName(cd);
        var utfHash = internalName.hashCode();
        var utf = tryFindUtf8(AbstractPoolEntry.hashString(utfHash), internalName);
        if (utf == null)
            utf = internalAdd(new AbstractPoolEntry.Utf8EntryImpl(this, size, internalName, utfHash));

        return internalAdd(new AbstractPoolEntry.ClassEntryImpl(this, size, utf, hash, cd));
    }

    private AbstractPoolEntry.ClassEntryImpl tryFindClassEntry(int hash, AbstractPoolEntry.Utf8EntryImpl utf8) {
        EntryMap map = map();
        for (int token = map.firstToken(hash); token != -1; token = map.nextToken(hash, token)) {
            PoolEntry e = entryByIndex(map.getIndexByToken(token));
            if (e.tag() == TAG_CLASS
                    && e instanceof AbstractPoolEntry.ClassEntryImpl ce
                    && ce.ref1.equalsUtf8(utf8))
                return ce;
        }
        if (!doneFullScan) {
            fullScan();
            return tryFindClassEntry(hash, utf8);
        }
        return null;
    }

    @Override
    public AbstractPoolEntry.Utf8EntryImpl utf8Entry(ClassDesc desc) {
        var utf8 = utf8Entry(desc.descriptorString());
        if (utf8.typeSym == null)
            utf8.typeSym = desc;
        return utf8;
    }

    @Override
    public Utf8Entry utf8Entry(MethodTypeDesc desc) {
        var utf8 = utf8Entry(desc.descriptorString());
        if (utf8.typeSym == null)
            utf8.typeSym = desc;
        return utf8;
    }

    @Override
    public AbstractPoolEntry.Utf8EntryImpl utf8Entry(String s) {
        int contentHash = s.hashCode();
        var ce = tryFindUtf8(AbstractPoolEntry.hashString(contentHash), s);
        return ce == null ? internalAdd(new AbstractPoolEntry.Utf8EntryImpl(this, size, s, contentHash)) : ce;
    }

    AbstractPoolEntry.Utf8EntryImpl maybeCloneUtf8Entry(Utf8Entry entry) {
        AbstractPoolEntry.Utf8EntryImpl e = (AbstractPoolEntry.Utf8EntryImpl) entry;
        if (e.constantPool == this || e.constantPool == parent)
            return e;
        AbstractPoolEntry.Utf8EntryImpl ce = tryFindUtf8(e.hashCode(), e);
        return ce == null ? internalAdd(new AbstractPoolEntry.Utf8EntryImpl(this, size, e)) : ce;
    }

    @Override
    public AbstractPoolEntry.ClassEntryImpl classEntry(Utf8Entry nameEntry) {
        var ne = maybeCloneUtf8Entry(nameEntry);
        return classEntry(ne, AbstractPoolEntry.isArrayDescriptor(ne));
    }

    AbstractPoolEntry.ClassEntryImpl classEntry(AbstractPoolEntry.Utf8EntryImpl ne, boolean isArray) {
        int hash = AbstractPoolEntry.hashClassFromUtf8(isArray, ne);
        var e = tryFindClassEntry(hash, ne);
        return e == null ? internalAdd(new AbstractPoolEntry.ClassEntryImpl(this, size, ne, hash,
                isArray && ne.typeSym instanceof ClassDesc cd ? cd : null)) : e;
    }

    @Override
    public ClassEntry classEntry(ClassDesc cd) {
        if (cd.isClassOrInterface()) { // implicit null check
            return classEntryForClassOrInterface(cd);
        }
        if (cd.isArray()) {
            return classEntry(utf8Entry(cd), true);
        }
        throw new IllegalArgumentException("Cannot be encoded as ClassEntry: " + cd.displayName());
    }

    AbstractPoolEntry.ClassEntryImpl cloneClassEntry(AbstractPoolEntry.ClassEntryImpl e) {
        var ce = tryFindClassEntry(e.hashCode(), e.ref1);
        if (ce != null) {
            return ce;
        }

        var utf8 = maybeCloneUtf8Entry(e.ref1); // call order matters
        return internalAdd(new AbstractPoolEntry.ClassEntryImpl(this, size,
                utf8, e.hashCode(), e.sym));
    }

    @Override
    public PackageEntry packageEntry(Utf8Entry nameEntry) {
        AbstractPoolEntry.Utf8EntryImpl ne = maybeCloneUtf8Entry(nameEntry);
        var e = (AbstractPoolEntry.PackageEntryImpl) findEntry(TAG_PACKAGE, ne);
        return e == null ? internalAdd(new AbstractPoolEntry.PackageEntryImpl(this, size, ne)) : e;
    }

    @Override
    public ModuleEntry moduleEntry(Utf8Entry nameEntry) {
        AbstractPoolEntry.Utf8EntryImpl ne = maybeCloneUtf8Entry(nameEntry);
        var e = (AbstractPoolEntry.ModuleEntryImpl) findEntry(TAG_MODULE, ne);
        return e == null ? internalAdd(new AbstractPoolEntry.ModuleEntryImpl(this, size, ne)) : e;
    }

    @Override
    public AbstractPoolEntry.NameAndTypeEntryImpl nameAndTypeEntry(Utf8Entry nameEntry, Utf8Entry typeEntry) {
        AbstractPoolEntry.Utf8EntryImpl ne = maybeCloneUtf8Entry(nameEntry);
        AbstractPoolEntry.Utf8EntryImpl te = maybeCloneUtf8Entry(typeEntry);
        var e = (AbstractPoolEntry.NameAndTypeEntryImpl) findEntry(TAG_NAME_AND_TYPE, ne, te);
        return e == null ? internalAdd(new AbstractPoolEntry.NameAndTypeEntryImpl(this, size, ne, te)) : e;
    }

    @Override
    public FieldRefEntry fieldRefEntry(ClassEntry owner, NameAndTypeEntry nameAndType) {
        var oe = AbstractPoolEntry.maybeClone(this, (AbstractPoolEntry.ClassEntryImpl) owner);
        var ne = AbstractPoolEntry.maybeClone(this, (AbstractPoolEntry.NameAndTypeEntryImpl) nameAndType);
        var e = (AbstractPoolEntry.FieldRefEntryImpl) findEntry(TAG_FIELDREF, oe, ne);
        return e == null ? internalAdd(new AbstractPoolEntry.FieldRefEntryImpl(this, size, oe, ne)) : e;
    }

    @Override
    public MethodRefEntry methodRefEntry(ClassEntry owner, NameAndTypeEntry nameAndType) {
        var oe = AbstractPoolEntry.maybeClone(this, (AbstractPoolEntry.ClassEntryImpl) owner);
        var ne = AbstractPoolEntry.maybeClone(this, (AbstractPoolEntry.NameAndTypeEntryImpl) nameAndType);
        var e = (AbstractPoolEntry.MethodRefEntryImpl) findEntry(TAG_METHODREF, oe, ne);
        return e == null ? internalAdd(new AbstractPoolEntry.MethodRefEntryImpl(this, size, oe, ne)) : e;
    }

    @Override
    public InterfaceMethodRefEntry interfaceMethodRefEntry(ClassEntry owner, NameAndTypeEntry nameAndType) {
        var oe = AbstractPoolEntry.maybeClone(this, (AbstractPoolEntry.ClassEntryImpl) owner);
        var ne = AbstractPoolEntry.maybeClone(this, (AbstractPoolEntry.NameAndTypeEntryImpl) nameAndType);
        var e = (AbstractPoolEntry.InterfaceMethodRefEntryImpl) findEntry(TAG_INTERFACE_METHODREF, oe, ne);
        return e == null ? internalAdd(new AbstractPoolEntry.InterfaceMethodRefEntryImpl(this, size, oe, ne)) : e;
    }

    @Override
    public MethodTypeEntry methodTypeEntry(MethodTypeDesc descriptor) {
        return methodTypeEntry(utf8Entry(descriptor));
    }

    @Override
    public MethodTypeEntry methodTypeEntry(Utf8Entry descriptor) {
        AbstractPoolEntry.Utf8EntryImpl de = maybeCloneUtf8Entry(descriptor);
        var e = (AbstractPoolEntry.MethodTypeEntryImpl) findEntry(TAG_METHOD_TYPE, de);
        return e == null ? internalAdd(new AbstractPoolEntry.MethodTypeEntryImpl(this, size, de)) : e;
    }

    @Override
    public MethodHandleEntry methodHandleEntry(int refKind, MemberRefEntry reference) {
        reference = AbstractPoolEntry.maybeClone(this, reference);
        int hash = AbstractPoolEntry.hash2(TAG_METHOD_HANDLE, refKind, reference.index());
        EntryMap map1 = map();
        for (int token = map1.firstToken(hash); token != -1; token = map1.nextToken(hash, token)) {
            PoolEntry e = entryByIndex(map1.getIndexByToken(token));
            if (e.tag() == TAG_METHOD_HANDLE
                && e instanceof AbstractPoolEntry.MethodHandleEntryImpl ce
                && ce.kind() == refKind && ce.reference() == reference)
                return ce;
        }
        if (!doneFullScan) {
            fullScan();
            return methodHandleEntry(refKind, reference);
        }
        return internalAdd(new AbstractPoolEntry.MethodHandleEntryImpl(this, size,
                hash, refKind, (AbstractPoolEntry.AbstractMemberRefEntry) reference), hash);
    }

    @Override
    public InvokeDynamicEntry invokeDynamicEntry(BootstrapMethodEntry bootstrapMethodEntry,
                                                 NameAndTypeEntry nameAndType) {
        if (!canWriteDirect(bootstrapMethodEntry.constantPool()))
            bootstrapMethodEntry = bsmEntry(bootstrapMethodEntry.bootstrapMethod(),
                                            bootstrapMethodEntry.arguments());
        nameAndType = AbstractPoolEntry.maybeClone(this, nameAndType);
        int hash = AbstractPoolEntry.hash2(TAG_INVOKE_DYNAMIC,
                bootstrapMethodEntry.bsmIndex(), nameAndType.index());
        EntryMap map1 = map();
        for (int token = map1.firstToken(hash); token != -1; token = map1.nextToken(hash, token)) {
            PoolEntry e = entryByIndex(map1.getIndexByToken(token));
            if (e.tag() == TAG_INVOKE_DYNAMIC
                && e instanceof AbstractPoolEntry.InvokeDynamicEntryImpl ce
                && ce.bootstrap() == bootstrapMethodEntry && ce.nameAndType() == nameAndType)
                return ce;
        }
        if (!doneFullScan) {
            fullScan();
            return invokeDynamicEntry(bootstrapMethodEntry, nameAndType);
        }

        AbstractPoolEntry.InvokeDynamicEntryImpl ce =
                new AbstractPoolEntry.InvokeDynamicEntryImpl(this, size, hash,
                        (BootstrapMethodEntryImpl) bootstrapMethodEntry,
                        (AbstractPoolEntry.NameAndTypeEntryImpl) nameAndType);
        internalAdd(ce, hash);
        return ce;
    }

    @Override
    public ConstantDynamicEntry constantDynamicEntry(BootstrapMethodEntry bootstrapMethodEntry,
                                                     NameAndTypeEntry nameAndType) {
        if (!canWriteDirect(bootstrapMethodEntry.constantPool()))
            bootstrapMethodEntry = bsmEntry(bootstrapMethodEntry.bootstrapMethod(),
                                            bootstrapMethodEntry.arguments());
        nameAndType = AbstractPoolEntry.maybeClone(this, nameAndType);
        int hash = AbstractPoolEntry.hash2(TAG_DYNAMIC,
                bootstrapMethodEntry.bsmIndex(), nameAndType.index());
        EntryMap map1 = map();
        for (int token = map1.firstToken(hash); token != -1; token = map1.nextToken(hash, token)) {
            PoolEntry e = entryByIndex(map1.getIndexByToken(token));
            if (e.tag() == TAG_DYNAMIC
                && e instanceof AbstractPoolEntry.ConstantDynamicEntryImpl ce
                && ce.bootstrap() == bootstrapMethodEntry && ce.nameAndType() == nameAndType)
                return ce;
        }
        if (!doneFullScan) {
            fullScan();
            return constantDynamicEntry(bootstrapMethodEntry, nameAndType);
        }

        AbstractPoolEntry.ConstantDynamicEntryImpl ce =
                new AbstractPoolEntry.ConstantDynamicEntryImpl(this, size, hash,
                        (BootstrapMethodEntryImpl) bootstrapMethodEntry,
                        (AbstractPoolEntry.NameAndTypeEntryImpl) nameAndType);
        internalAdd(ce, hash);
        return ce;
    }

    @Override
    public IntegerEntry intEntry(int value) {
        var e = findIntEntry(value);
        return e == null ? internalAdd(new AbstractPoolEntry.IntegerEntryImpl(this, size, value)) : e;
    }

    @Override
    public FloatEntry floatEntry(float value) {
        var e = findFloatEntry(value);
        return e == null ? internalAdd(new AbstractPoolEntry.FloatEntryImpl(this, size, value)) : e;
    }

    @Override
    public LongEntry longEntry(long value) {
        var e = findLongEntry(value);
        return e == null ? internalAdd(new AbstractPoolEntry.LongEntryImpl(this, size, value)) : e;
    }

    @Override
    public DoubleEntry doubleEntry(double value) {
        var e = findDoubleEntry(value);
        return e == null ? internalAdd(new AbstractPoolEntry.DoubleEntryImpl(this, size, value)) : e;
    }

    @Override
    public StringEntry stringEntry(Utf8Entry utf8) {
        AbstractPoolEntry.Utf8EntryImpl ue = maybeCloneUtf8Entry(utf8);
        var e = (AbstractPoolEntry.StringEntryImpl) findEntry(TAG_STRING, ue);
        return e == null ? internalAdd(new AbstractPoolEntry.StringEntryImpl(this, size, ue)) : e;
    }

    @Override
    public BootstrapMethodEntry bsmEntry(MethodHandleEntry methodReference,
                                         List arguments) {
        methodReference = AbstractPoolEntry.maybeClone(this, methodReference);
        for (LoadableConstantEntry a : arguments) {
            if (!canWriteDirect(a.constantPool())) {
                // copy args list
                LoadableConstantEntry[] arr = arguments.toArray(new LoadableConstantEntry[0]);
                for (int i = 0; i < arr.length; i++)
                    arr[i] = AbstractPoolEntry.maybeClone(this, arr[i]);
                arguments = List.of(arr);

                break;
            }
        }
        AbstractPoolEntry.MethodHandleEntryImpl mre = (AbstractPoolEntry.MethodHandleEntryImpl) methodReference;
        int hash = BootstrapMethodEntryImpl.computeHashCode(mre, arguments);
        EntryMap map = bsmMap();
        for (int token = map.firstToken(hash); token != -1; token = map.nextToken(hash, token)) {
            BootstrapMethodEntryImpl e = bootstrapMethodEntry(map.getIndexByToken(token));
            if (e.bootstrapMethod() == mre && e.arguments().equals(arguments)) {
                return e;
            }
        }
        BootstrapMethodEntryImpl ne = new BootstrapMethodEntryImpl(this, bsmSize, hash, mre, arguments);
        return internalAdd(ne, hash);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy