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

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

There is a newer version: 24.beta3
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 java.lang.constant.*;
import java.lang.invoke.TypeDescriptor;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

import io.github.dmlloyd.classfile.ClassFile;
import io.github.dmlloyd.classfile.constantpool.ClassEntry;
import io.github.dmlloyd.classfile.constantpool.ConstantDynamicEntry;
import io.github.dmlloyd.classfile.constantpool.ConstantPool;
import io.github.dmlloyd.classfile.constantpool.ConstantPoolBuilder;
import io.github.dmlloyd.classfile.constantpool.DoubleEntry;
import io.github.dmlloyd.classfile.constantpool.FieldRefEntry;
import io.github.dmlloyd.classfile.constantpool.FloatEntry;
import io.github.dmlloyd.classfile.constantpool.IntegerEntry;
import io.github.dmlloyd.classfile.constantpool.InterfaceMethodRefEntry;
import io.github.dmlloyd.classfile.constantpool.InvokeDynamicEntry;
import io.github.dmlloyd.classfile.constantpool.LongEntry;
import io.github.dmlloyd.classfile.constantpool.MemberRefEntry;
import io.github.dmlloyd.classfile.constantpool.MethodHandleEntry;
import io.github.dmlloyd.classfile.constantpool.MethodRefEntry;
import io.github.dmlloyd.classfile.constantpool.MethodTypeEntry;
import io.github.dmlloyd.classfile.constantpool.ModuleEntry;
import io.github.dmlloyd.classfile.constantpool.NameAndTypeEntry;
import io.github.dmlloyd.classfile.constantpool.PackageEntry;
import io.github.dmlloyd.classfile.constantpool.PoolEntry;
import io.github.dmlloyd.classfile.constantpool.StringEntry;
import io.github.dmlloyd.classfile.constantpool.Utf8Entry;
import io.github.dmlloyd.classfile.extras.constant.ModuleDesc;
import io.github.dmlloyd.classfile.extras.constant.PackageDesc;

public abstract sealed class AbstractPoolEntry {
    /*
    Invariant: a {CP,BSM} entry for pool P refer only to {CP,BSM} entries
    from P or P's parent.  This is enforced by the various xxxEntry methods
    in SplitConstantPool.  As a result, code in this file can use writeU2
    instead of writeIndex.

    Cloning of entries may be a no-op if the entry is already on the right pool
    (which implies that the referenced entries will also be on the right pool.)
     */

    private static final int TAG_SMEAR = 0x13C4B2D1;
    static final int NON_ZERO = 0x40000000;

    public static int hash1(int tag, int x1) {
        return (tag * TAG_SMEAR + x1) | NON_ZERO;
    }

    public static int hash2(int tag, int x1, int x2) {
        return (tag * TAG_SMEAR + x1 + 31 * x2) | NON_ZERO;
    }

    // Ensure that hash is never zero
    public static int hashString(int stringHash) {
        return stringHash | NON_ZERO;
    }

    public static Utf8Entry rawUtf8EntryFromStandardAttributeName(String name) {
        //assuming standard attribute names are all US_ASCII
        var raw = name.getBytes(StandardCharsets.US_ASCII);
        return new Utf8EntryImpl(null, 0, raw, 0, raw.length);
    }

    @SuppressWarnings("unchecked")
    public static  T maybeClone(ConstantPoolBuilder cp, T entry) {
        return (T)((AbstractPoolEntry)entry).clone(cp);
    }

    final ConstantPool constantPool;
    public final byte tag;
    private final int index;
    private final int hash;

    private AbstractPoolEntry(ConstantPool constantPool, int tag, int index, int hash) {
        this.tag = (byte) tag;
        this.index = index;
        this.hash = hash;
        this.constantPool = constantPool;
    }

    public ConstantPool constantPool() { return constantPool; }

    public int index() { return index; }

    @Override
    public int hashCode() {
        return hash;
    }

    public byte tag() {
        return tag;
    }

    public int width() {
        return (tag == ClassFile.TAG_LONG || tag == ClassFile.TAG_DOUBLE) ? 2 : 1;
    }

    abstract void writeTo(BufWriterImpl buf);

    abstract PoolEntry clone(ConstantPoolBuilder cp);

    public static final class Utf8EntryImpl extends AbstractPoolEntry implements Utf8Entry {
        // Processing UTF8 from the constant pool is one of the more expensive
        // operations, and often, we don't actually need access to the constant
        // as a string.  So there are multiple layers of laziness in UTF8
        // constants.  In the first stage, all we do is record the range of
        // bytes in the classfile.  If the size or hashCode is needed, then we
        // process the raw bytes into a byte[] or char[], but do not inflate
        // a String.  If a string is needed, it too is inflated lazily.
        // If we construct a Utf8Entry from a string, we generate the encoding
        // at write time.

        enum State { RAW, BYTE, CHAR, STRING }

        private State state;
        private final byte[] rawBytes; // null if initialized directly from a string
        private final int offset;
        private final int rawLen;
        // Set in any state other than RAW
        private int hash;
        private int charLen;
        // Set in CHAR state
        private char[] chars;
        // Only set in STRING state
        private String stringValue;

        Utf8EntryImpl(ConstantPool cpm, int index,
                          byte[] rawBytes, int offset, int rawLen) {
            super(cpm, ClassFile.TAG_UTF8, index, 0);
            this.rawBytes = rawBytes;
            this.offset = offset;
            this.rawLen = rawLen;
            this.state = State.RAW;
        }

        Utf8EntryImpl(ConstantPool cpm, int index, String s) {
            this(cpm, index, s, hashString(s.hashCode()));
        }

        Utf8EntryImpl(ConstantPool cpm, int index, String s, int hash) {
            super(cpm, ClassFile.TAG_UTF8, index, 0);
            this.rawBytes = null;
            this.offset = 0;
            this.rawLen = 0;
            this.state = State.STRING;
            this.stringValue = s;
            this.charLen = s.length();
            this.hash = hash;
        }

        Utf8EntryImpl(ConstantPool cpm, int index, Utf8EntryImpl u) {
            super(cpm, ClassFile.TAG_UTF8, index, 0);
            this.rawBytes = u.rawBytes;
            this.offset = u.offset;
            this.rawLen = u.rawLen;
            this.state = u.state;
            this.hash = u.hash;
            this.charLen = u.charLen;
            this.chars = u.chars;
            this.stringValue = u.stringValue;
        }

        /**
         * {@jvms 4.4.7} String content is encoded in modified UTF-8.
         *
         * Modified UTF-8 strings are encoded so that code point sequences that
         * contain only non-null ASCII characters can be represented using only 1
         * byte per code point, but all code points in the Unicode codespace can be
         * represented.
         *
         * Modified UTF-8 strings are not null-terminated.
         *
         * Code points in the range '\u0001' to '\u007F' are represented by a single
         * byte.
         *
         * The null code point ('\u0000') and code points in the range '\u0080' to
         * '\u07FF' are represented by a pair of bytes.
         *
         * Code points in the range '\u0800' to '\uFFFF' are represented by 3 bytes.
         *
         * Characters with code points above U+FFFF (so-called supplementary
         * characters) are represented by separately encoding the two surrogate code
         * units of their UTF-16 representation. Each of the surrogate code units is
         * represented by three bytes. This means supplementary characters are
         * represented by six bytes.
         *
         * The bytes of multibyte characters are stored in the class file in
         * big-endian (high byte first) order.
         *
         * There are two differences between this format and the "standard" UTF-8
         * format. First, the null character (char)0 is encoded using the 2-byte
         * format rather than the 1-byte format, so that modified UTF-8 strings
         * never have embedded nulls. Second, only the 1-byte, 2-byte, and 3-byte
         * formats of standard UTF-8 are used. The Java Virtual Machine does not
         * recognize the four-byte format of standard UTF-8; it uses its own
         * two-times-three-byte format instead.
         */
        private void inflate() {
            int hash = 0;
            boolean foundHigh = false;

            int px = offset;
            int utfend = px + rawLen;
            while (px < utfend) {
                int c = (int) rawBytes[px] & 0xff;
                if (c > 127) {
                    foundHigh = true;
                    break;
                }
                hash = 31 * hash + c;
                px++;
            }

            if (!foundHigh) {
                this.hash = hashString(hash);
                charLen = rawLen;
                state = State.BYTE;
            }
            else {
                char[] chararr = new char[rawLen];
                int chararr_count = 0;
                // Inflate prefix of bytes to characters
                for (int i = offset; i < px; i++) {
                    int c = (int) rawBytes[i] & 0xff;
                    chararr[chararr_count++] = (char) c;
                }
                while (px < utfend) {
                    int c = (int) rawBytes[px] & 0xff;
                    switch (c >> 4) {
                        case 0, 1, 2, 3, 4, 5, 6, 7: {
                            // 0xxx xxxx
                            px++;
                            chararr[chararr_count++] = (char) c;
                            hash = 31 * hash + c;
                            break;
                        }
                        case 12, 13: {
                            // 110x xxxx  10xx xxxx
                            px += 2;
                            if (px > utfend) {
                                throw new CpException("malformed input: partial character at end");
                            }
                            int char2 = rawBytes[px - 1];
                            if ((char2 & 0xC0) != 0x80) {
                                throw new CpException("malformed input around byte " + px);
                            }
                            char v = (char) (((c & 0x1F) << 6) | (char2 & 0x3F));
                            chararr[chararr_count++] = v;
                            hash = 31 * hash + v;
                            break;
                        }
                        case 14: {
                            // 1110 xxxx  10xx xxxx  10xx xxxx
                            px += 3;
                            if (px > utfend) {
                                throw new CpException("malformed input: partial character at end");
                            }
                            int char2 = rawBytes[px - 2];
                            int char3 = rawBytes[px - 1];
                            if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80)) {
                                throw new CpException("malformed input around byte " + (px - 1));
                            }
                            char v = (char) (((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | (char3 & 0x3F));
                            chararr[chararr_count++] = v;
                            hash = 31 * hash + v;
                            break;
                        }
                        default:
                            // 10xx xxxx,  1111 xxxx
                            throw new CpException("malformed input around byte " + px);
                    }
                }
                this.hash = hashString(hash);
                charLen = chararr_count;
                this.chars = chararr;
                state = State.CHAR;
            }

        }

        @Override
        public Utf8EntryImpl clone(ConstantPoolBuilder cp) {
            if (cp.canWriteDirect(constantPool))
                return this;
            return (state == State.STRING && rawBytes == null)
                   ? (Utf8EntryImpl) cp.utf8Entry(stringValue)
                   : ((SplitConstantPool) cp).maybeCloneUtf8Entry(this);
        }

        @Override
        public int hashCode() {
            if (state == State.RAW)
                inflate();
            return hash;
        }

        @Override
        public String toString() {
            if (state == State.RAW)
                inflate();
            if (state != State.STRING) {
                stringValue = (chars != null)
                              ? new String(chars, 0, charLen)
                              : new String(rawBytes, offset, charLen, StandardCharsets.ISO_8859_1);
                state = State.STRING;
            }
            return stringValue;
        }

        @Override
        public String stringValue() {
            return toString();
        }

        @Override
        public ConstantDesc constantValue() {
            return stringValue();
        }

        @Override
        public int length() {
            if (state == State.RAW)
                inflate();
            return charLen;
        }

        @Override
        public char charAt(int index) {
            if (state == State.STRING)
                return stringValue.charAt(index);
            if (state == State.RAW)
                inflate();
            return (chars != null)
                   ? chars[index]
                   : (char) rawBytes[index + offset];
        }

        @Override
        public CharSequence subSequence(int start, int end) {
            return toString().subSequence(start, end);
        }

        @Override
        public boolean equals(Object o) {
            if (o == this) return true;
            if (o instanceof Utf8EntryImpl u) {
                return equalsUtf8(u);
            }
            return false;
        }

        public boolean equalsUtf8(Utf8EntryImpl u) {
            if (hashCode() != u.hashCode()
                || length() != u.length())
                return false;
            if (rawBytes != null && u.rawBytes != null)
                return Arrays.equals(rawBytes, offset, offset + rawLen,
                                     u.rawBytes, u.offset, u.offset + u.rawLen);
            else if ((state == State.STRING && u.state == State.STRING))
                return stringValue.equals(u.stringValue);
            else
                return stringValue().equals(u.stringValue());
        }

        @Override
        public boolean equalsString(String s) {
            if (state == State.RAW)
                inflate();
            switch (state) {
                case STRING:
                    return stringValue.equals(s);
                case CHAR:
                    if (charLen != s.length() || hash != hashString(s.hashCode()))
                        return false;
                    for (int i=0; i 65535) {
                    throw new IllegalArgumentException("string too long");
                }
                pool.writeU1(tag);
                pool.writeU2(charLen);
                for (int i = 0; i < charLen; ++i) {
                    char c = stringValue.charAt(i);
                    if (c >= '\001' && c <= '\177') {
                        // Optimistic writing -- hope everything is bytes
                        // If not, we bail out, and alternate path patches the length
                        pool.writeU1((byte) c);
                    }
                    else {
                        int charLength = stringValue.length();
                        int byteLength = i;
                        char c1;
                        for (int j = i; j < charLength; ++j) {
                            c1 = (stringValue).charAt(j);
                            if (c1 >= '\001' && c1 <= '\177') {
                                byteLength++;
                            } else if (c1 > '\u07FF') {
                                byteLength += 3;
                            } else {
                                byteLength += 2;
                            }
                        }
                        if (byteLength > 65535) {
                            throw new IllegalArgumentException();
                        }
                        int byteLengthFinal = byteLength;
                        pool.patchInt(pool.size() - i - 2, 2, byteLengthFinal);
                        for (int j = i; j < charLength; ++j) {
                            c1 = (stringValue).charAt(j);
                            if (c1 >= '\001' && c1 <= '\177') {
                                pool.writeU1((byte) c1);
                            } else if (c1 > '\u07FF') {
                                pool.writeU1((byte) (0xE0 | c1 >> 12 & 0xF));
                                pool.writeU1((byte) (0x80 | c1 >> 6 & 0x3F));
                                pool.writeU1((byte) (0x80 | c1 & 0x3F));
                            } else {
                                pool.writeU1((byte) (0xC0 | c1 >> 6 & 0x1F));
                                pool.writeU1((byte) (0x80 | c1 & 0x3F));
                            }
                        }
                        break;
                    }
                }
            }
        }
    }

    abstract static sealed class AbstractRefEntry extends AbstractPoolEntry {
        protected final T ref1;

        public AbstractRefEntry(ConstantPool constantPool, int tag, int index, T ref1) {
            super(constantPool, tag, index, hash1(tag, ref1.index()));
            this.ref1 = ref1;
        }

        public T ref1() {
            return ref1;
        }

        void writeTo(BufWriterImpl pool) {
            pool.writeU1(tag);
            pool.writeU2(ref1.index());
        }

        @Override
        public String toString() {
            return tag() + " " + ref1();
        }
    }

    abstract static sealed class AbstractRefsEntry
            extends AbstractPoolEntry {
        protected final T ref1;
        protected final U ref2;

        public AbstractRefsEntry(ConstantPool constantPool, int tag, int index, T ref1, U ref2) {
            super(constantPool, tag, index, hash2(tag, ref1.index(), ref2.index()));
            this.ref1 = ref1;
            this.ref2 = ref2;
        }

        public T ref1() {
            return ref1;
        }

        public U ref2() {
            return ref2;
        }

        void writeTo(BufWriterImpl pool) {
            pool.writeU1(tag);
            pool.writeU2(ref1.index());
            pool.writeU2(ref2.index());
        }

        @Override
        public String toString() {
            return tag() + " " + ref1 + "-" + ref2;
        }
    }

    abstract static sealed class AbstractNamedEntry extends AbstractRefEntry {

        public AbstractNamedEntry(ConstantPool constantPool, int tag, int index, Utf8EntryImpl ref1) {
            super(constantPool, tag, index, ref1);
        }

        public Utf8Entry name() {
            return ref1;
        }

        public String asInternalName() {
            return ref1.stringValue();
        }
    }

    public static final class ClassEntryImpl extends AbstractNamedEntry implements ClassEntry {

        public ClassDesc sym = null;

        ClassEntryImpl(ConstantPool cpm, int index, Utf8EntryImpl name) {
            super(cpm, ClassFile.TAG_CLASS, index, name);
        }

        @Override
        public ClassEntry clone(ConstantPoolBuilder cp) {
            if (cp.canWriteDirect(constantPool)) {
                return this;
            } else {
                ClassEntryImpl ret = (ClassEntryImpl)cp.classEntry(ref1);
                ret.sym = sym;
                return ret;
            }
        }

        @Override
        public ClassDesc asSymbol() {
            var sym = this.sym;
            if (sym != null) {
                return sym;
            }
            return this.sym = Util.toClassDesc(asInternalName());
        }

        @Override
        public boolean equals(Object o) {
            if (o == this) return true;
            if (o instanceof ClassEntryImpl cce) {
                return cce.name().equals(this.name());
            } else if (o instanceof ClassEntry c) {
                return c.asSymbol().equals(this.asSymbol());
            }
            return false;
        }
    }

    public static final class PackageEntryImpl extends AbstractNamedEntry implements PackageEntry {

        PackageEntryImpl(ConstantPool cpm, int index, Utf8EntryImpl name) {
            super(cpm, ClassFile.TAG_PACKAGE, index, name);
        }

        @Override
        public PackageEntry clone(ConstantPoolBuilder cp) {
            return cp.canWriteDirect(constantPool) ? this : cp.packageEntry(ref1);
        }

        @Override
        public PackageDesc asSymbol() {
            return PackageDesc.ofInternalName(asInternalName());
        }

        @Override
        public boolean equals(Object o) {
            if (o == this) return true;
            if (o instanceof PackageEntry p) {
                return name().equals(p.name());
            }
            return false;
        }
    }

    public static final class ModuleEntryImpl extends AbstractNamedEntry implements ModuleEntry {

        ModuleEntryImpl(ConstantPool cpm, int index, Utf8EntryImpl name) {
            super(cpm, ClassFile.TAG_MODULE, index, name);
        }

        @Override
        public ModuleEntry clone(ConstantPoolBuilder cp) {
            return cp.canWriteDirect(constantPool) ? this : cp.moduleEntry(ref1);
        }

        @Override
        public ModuleDesc asSymbol() {
            return ModuleDesc.of(asInternalName());
        }

        @Override
        public boolean equals(Object o) {
            if (o == this) return true;
            if (o instanceof ModuleEntryImpl m) {
                return name().equals(m.name());
            }
            return false;
        }
    }

    public static final class NameAndTypeEntryImpl extends AbstractRefsEntry
            implements NameAndTypeEntry {

        public TypeDescriptor typeSym = null;

        NameAndTypeEntryImpl(ConstantPool cpm, int index, Utf8EntryImpl name, Utf8EntryImpl type) {
            super(cpm, ClassFile.TAG_NAMEANDTYPE, index, name, type);
        }

        @Override
        public Utf8Entry name() {
            return ref1;
        }

        @Override
        public Utf8Entry type() {
            return ref2;
        }

        public ClassDesc fieldTypeSymbol() {
            if (typeSym instanceof ClassDesc cd) {
                return cd;
            } else {
                return (ClassDesc)(typeSym = ClassDesc.ofDescriptor(ref2.stringValue()));
            }
        }

        public MethodTypeDesc methodTypeSymbol() {
            if (typeSym instanceof MethodTypeDesc mtd) {
                return mtd;
            } else {
                return (MethodTypeDesc)(typeSym = MethodTypeDesc.ofDescriptor(ref2.stringValue()));
            }
        }

        @Override
        public NameAndTypeEntry clone(ConstantPoolBuilder cp) {
            if (cp.canWriteDirect(constantPool)) {
                return this;
            } else {
                var ret = (NameAndTypeEntryImpl)cp.nameAndTypeEntry(ref1, ref2);
                ret.typeSym = typeSym;
                return ret;
            }
        }

        @Override
        public boolean equals(Object o) {
            if (o == this) return true;
            if (o instanceof NameAndTypeEntryImpl nat) {
                return name().equals(nat.name()) && type().equals(nat.type());
            }
            return false;
        }
    }

    public abstract static sealed class AbstractMemberRefEntry
            extends AbstractRefsEntry
            implements MemberRefEntry {

        AbstractMemberRefEntry(ConstantPool cpm, int tag, int index, ClassEntryImpl owner,
                       NameAndTypeEntryImpl nameAndType) {
            super(cpm, tag, index, owner, nameAndType);
        }

        @Override
        public ClassEntryImpl owner() {
            return ref1;
        }

        @Override
        public NameAndTypeEntryImpl nameAndType() {
            return ref2;
        }

        @Override
        public String toString() {
            return tag() + " " + owner().asInternalName() + "." + nameAndType().name().stringValue()
                   + "-" + nameAndType().type().stringValue();
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o instanceof AbstractMemberRefEntry m) {
                return tag == m.tag()
                && owner().equals(m.owner())
                && nameAndType().equals(m.nameAndType());
            }
            return false;
        }
    }

    public static final class FieldRefEntryImpl extends AbstractMemberRefEntry implements FieldRefEntry {

        FieldRefEntryImpl(ConstantPool cpm, int index,
                              ClassEntryImpl owner, NameAndTypeEntryImpl nameAndType) {
            super(cpm, ClassFile.TAG_FIELDREF, index, owner, nameAndType);
        }

        @Override
        public FieldRefEntry clone(ConstantPoolBuilder cp) {
            return cp.canWriteDirect(constantPool) ? this : cp.fieldRefEntry(ref1, ref2);
        }
    }

    public static final class MethodRefEntryImpl extends AbstractMemberRefEntry implements MethodRefEntry {

        MethodRefEntryImpl(ConstantPool cpm, int index,
                               ClassEntryImpl owner, NameAndTypeEntryImpl nameAndType) {
            super(cpm, ClassFile.TAG_METHODREF, index, owner, nameAndType);
        }

        @Override
        public MethodRefEntry clone(ConstantPoolBuilder cp) {
            return cp.canWriteDirect(constantPool) ? this : cp.methodRefEntry(ref1, ref2);
        }
    }

    public static final class InterfaceMethodRefEntryImpl extends AbstractMemberRefEntry implements InterfaceMethodRefEntry {

        InterfaceMethodRefEntryImpl(ConstantPool cpm, int index, ClassEntryImpl owner,
                                        NameAndTypeEntryImpl nameAndType) {
            super(cpm, ClassFile.TAG_INTERFACEMETHODREF, index, owner, nameAndType);
        }

        @Override
        public InterfaceMethodRefEntry clone(ConstantPoolBuilder cp) {
            return cp.canWriteDirect(constantPool) ? this : cp.interfaceMethodRefEntry(ref1, ref2);
        }
    }

    public abstract static sealed class AbstractDynamicConstantPoolEntry extends AbstractPoolEntry {

        private final int bsmIndex;
        private BootstrapMethodEntryImpl bootstrapMethod;
        private final NameAndTypeEntryImpl nameAndType;

        AbstractDynamicConstantPoolEntry(ConstantPool cpm, int tag, int index, int hash, BootstrapMethodEntryImpl bootstrapMethod,
                                         NameAndTypeEntryImpl nameAndType) {
            super(cpm, tag, index, hash);
            this.bsmIndex = bootstrapMethod.bsmIndex();
            this.bootstrapMethod = bootstrapMethod;
            this.nameAndType = nameAndType;
        }

        AbstractDynamicConstantPoolEntry(ConstantPool cpm, int tag, int index, int hash, int bsmIndex,
                                         NameAndTypeEntryImpl nameAndType) {
            super(cpm, tag, index, hash);
            this.bsmIndex = bsmIndex;
            this.bootstrapMethod = null;
            this.nameAndType = nameAndType;
        }

        /**
         * @return the bootstrapMethod
         */
        public BootstrapMethodEntryImpl bootstrap() {
            if (bootstrapMethod == null) {
                bootstrapMethod = (BootstrapMethodEntryImpl) constantPool.bootstrapMethodEntry(bsmIndex);
            }
            return bootstrapMethod;
        }

        /**
         * @return the bsmIndex
         */
        public int bootstrapMethodIndex() {
            return bsmIndex;
        }

        /**
         * @return the nameAndType
         */
        public NameAndTypeEntryImpl nameAndType() {
            return nameAndType;
        }

        void writeTo(BufWriterImpl pool) {
            pool.writeU1(tag);
            pool.writeU2(bsmIndex);
            pool.writeU2(nameAndType.index());
        }

        @Override
        public String toString() {
            return tag() + " " + bootstrap() + "." + nameAndType().name().stringValue()
                   + "-" + nameAndType().type().stringValue();
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o instanceof AbstractDynamicConstantPoolEntry d) {
                return this.tag() == d.tag()
                && bootstrap().equals(d.bootstrap())
                && nameAndType.equals(d.nameAndType());
            }
            return false;
        }
    }

    public static final class InvokeDynamicEntryImpl
            extends AbstractDynamicConstantPoolEntry
            implements InvokeDynamicEntry {

        InvokeDynamicEntryImpl(ConstantPool cpm, int index, int hash, BootstrapMethodEntryImpl bootstrapMethod,
                                   NameAndTypeEntryImpl nameAndType) {
            super(cpm, ClassFile.TAG_INVOKEDYNAMIC, index, hash, bootstrapMethod, nameAndType);
        }

        InvokeDynamicEntryImpl(ConstantPool cpm, int index, int bsmIndex,
                                   NameAndTypeEntryImpl nameAndType) {
            super(cpm, ClassFile.TAG_INVOKEDYNAMIC, index, hash2(ClassFile.TAG_INVOKEDYNAMIC, bsmIndex, nameAndType.index()),
                  bsmIndex, nameAndType);
        }

        @Override
        public InvokeDynamicEntry clone(ConstantPoolBuilder cp) {
            return cp.canWriteDirect(constantPool) ? this : cp.invokeDynamicEntry(bootstrap(), nameAndType());
        }
    }

    public static final class ConstantDynamicEntryImpl extends AbstractDynamicConstantPoolEntry
            implements ConstantDynamicEntry {

        ConstantDynamicEntryImpl(ConstantPool cpm, int index, int hash, BootstrapMethodEntryImpl bootstrapMethod,
                                     NameAndTypeEntryImpl nameAndType) {
            super(cpm, ClassFile.TAG_CONSTANTDYNAMIC, index, hash, bootstrapMethod, nameAndType);
        }

        ConstantDynamicEntryImpl(ConstantPool cpm, int index, int bsmIndex,
                                     NameAndTypeEntryImpl nameAndType) {
            super(cpm, ClassFile.TAG_CONSTANTDYNAMIC, index, hash2(ClassFile.TAG_CONSTANTDYNAMIC, bsmIndex, nameAndType.index()),
                  bsmIndex, nameAndType);
        }

        @Override
        public ConstantDynamicEntry clone(ConstantPoolBuilder cp) {
            return cp.canWriteDirect(constantPool) ? this : cp.constantDynamicEntry(bootstrap(), nameAndType());
        }
    }

    public static final class MethodHandleEntryImpl extends AbstractPoolEntry
            implements MethodHandleEntry {

        private final int refKind;
        private final AbstractMemberRefEntry reference;

        MethodHandleEntryImpl(ConstantPool cpm, int index, int hash, int refKind, AbstractMemberRefEntry
                reference) {
            super(cpm, ClassFile.TAG_METHODHANDLE, index, hash);
            this.refKind = refKind;
            this.reference = reference;
        }

        MethodHandleEntryImpl(ConstantPool cpm, int index, int refKind, AbstractMemberRefEntry
                reference) {
            super(cpm, ClassFile.TAG_METHODHANDLE, index, hash2(ClassFile.TAG_METHODHANDLE, refKind, reference.index()));
            this.refKind = refKind;
            this.reference = reference;
        }

        @Override
        public int kind() {
            return refKind;
        }

        @Override
        public AbstractMemberRefEntry reference() {
            return reference;
        }

        @Override
        public DirectMethodHandleDesc asSymbol() {
            return MethodHandleDesc.of(
                    DirectMethodHandleDesc.Kind.valueOf(kind(), reference() instanceof InterfaceMethodRefEntry),
                    ((MemberRefEntry) reference()).owner().asSymbol(),
                    ((MemberRefEntry) reference()).nameAndType().name().stringValue(),
                    ((MemberRefEntry) reference()).nameAndType().type().stringValue());
        }

        @Override
        void writeTo(BufWriterImpl pool) {
            pool.writeU1(tag);
            pool.writeU1(refKind);
            pool.writeU2(reference.index());
        }

        @Override
        public MethodHandleEntry clone(ConstantPoolBuilder cp) {
            return cp.canWriteDirect(constantPool) ? this : cp.methodHandleEntry(refKind, reference);
        }

        @Override
        public String toString() {
            return tag() + " " + kind() + ":" + ((MemberRefEntry) reference()).owner().asInternalName() + "." + ((MemberRefEntry) reference()).nameAndType().name().stringValue()
                   + "-" + ((MemberRefEntry) reference()).nameAndType().type().stringValue();
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o instanceof MethodHandleEntryImpl m) {
                return kind() == m.kind()
                && reference.equals(m.reference());
            }
            return false;
        }
    }

    public static final class MethodTypeEntryImpl
            extends AbstractRefEntry
            implements MethodTypeEntry {

        public MethodTypeDesc sym = null;

        MethodTypeEntryImpl(ConstantPool cpm, int index, Utf8EntryImpl descriptor) {
            super(cpm, ClassFile.TAG_METHODTYPE, index, descriptor);
        }

        @Override
        public Utf8Entry descriptor() {
            return ref1;
        }

        @Override
        public MethodTypeEntry clone(ConstantPoolBuilder cp) {
            if (cp.canWriteDirect(constantPool)) {
                return this;
            } else {
                var ret = (MethodTypeEntryImpl)cp.methodTypeEntry(ref1);
                ret.sym = sym;
                return ret;
            }
        }

        @Override
        public MethodTypeDesc asSymbol() {
            var sym = this.sym;
            if (sym != null) {
                return sym;
            }
            return this.sym = MethodTypeDesc.ofDescriptor(descriptor().stringValue());
        }

        @Override
        public boolean equals(Object o) {
            if (o == this) return true;
            if (o instanceof MethodTypeEntryImpl m) {
                return descriptor().equals(m.descriptor());
            }
            return false;
        }
    }

    public static final class StringEntryImpl
            extends AbstractRefEntry
            implements StringEntry {

        StringEntryImpl(ConstantPool cpm, int index, Utf8EntryImpl utf8) {
            super(cpm, ClassFile.TAG_STRING, index, utf8);
        }

        @Override
        public Utf8EntryImpl utf8() {
            return ref1;
        }

        @Override
        public String stringValue() {
            return ref1.toString();
        }

        @Override
        public ConstantDesc constantValue() {
            return stringValue();
        }

        @Override
        public StringEntry clone(ConstantPoolBuilder cp) {
            return cp.canWriteDirect(constantPool) ? this : cp.stringEntry(ref1);
        }

        @Override
        public String toString() {
            return tag() + " \"" + stringValue() + "\"";
        }

        @Override
        public boolean equals(Object o) {
            if (o == this) return true;
            if (o instanceof StringEntryImpl s) {
                // check utf8 rather allocating a string
                return utf8().equals(s.utf8());
            }
            return false;
        }


    }

    public static final class IntegerEntryImpl extends AbstractPoolEntry implements IntegerEntry {

        private final int val;

        IntegerEntryImpl(ConstantPool cpm, int index, int i) {
            super(cpm, ClassFile.TAG_INTEGER, index, hash1(ClassFile.TAG_INTEGER, Integer.hashCode(i)));
            val = i;
        }

        @Override
        void writeTo(BufWriterImpl pool) {
            pool.writeU1(tag);
            pool.writeInt(val);
        }

        @Override
        public IntegerEntry clone(ConstantPoolBuilder cp) {
            return cp.canWriteDirect(constantPool) ? this : cp.intEntry(val);
        }

        @Override
        public int intValue() {
            return val;
        }

        @Override
        public ConstantDesc constantValue() {
            return val;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o instanceof IntegerEntryImpl e) {
                return intValue() == e.intValue();
            }
            return false;
        }
    }

    public static final class FloatEntryImpl extends AbstractPoolEntry
            implements FloatEntry {

        private final float val;

        FloatEntryImpl(ConstantPool cpm, int index, float f) {
            super(cpm, ClassFile.TAG_FLOAT, index, hash1(ClassFile.TAG_FLOAT, Float.hashCode(f)));
            val = f;
        }

        @Override
        void writeTo(BufWriterImpl pool) {
            pool.writeU1(tag);
            pool.writeFloat(val);
        }

        @Override
        public FloatEntry clone(ConstantPoolBuilder cp) {
            return cp.canWriteDirect(constantPool) ? this : cp.floatEntry(val);
        }

        @Override
        public float floatValue() {
            return val;
        }

        @Override
        public ConstantDesc constantValue() {
            return val;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o instanceof FloatEntryImpl e) {
                return floatValue() == e.floatValue();
            }
            return false;
        }
    }

    public static final class LongEntryImpl extends AbstractPoolEntry implements LongEntry {

        private final long val;

        LongEntryImpl(ConstantPool cpm, int index, long l) {
            super(cpm, ClassFile.TAG_LONG, index, hash1(ClassFile.TAG_LONG, Long.hashCode(l)));
            val = l;
        }

        @Override
        void writeTo(BufWriterImpl pool) {
            pool.writeU1(tag);
            pool.writeLong(val);
        }

        @Override
        public LongEntry clone(ConstantPoolBuilder cp) {
            return cp.canWriteDirect(constantPool) ? this : cp.longEntry(val);
        }

        @Override
        public long longValue() {
            return val;
        }

        @Override
        public ConstantDesc constantValue() {
            return val;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o instanceof LongEntryImpl e) {
                return longValue() == e.longValue();
            }
            return false;
        }
    }

    public static final class DoubleEntryImpl extends AbstractPoolEntry implements DoubleEntry {

        private final double val;

        DoubleEntryImpl(ConstantPool cpm, int index, double d) {
            super(cpm, ClassFile.TAG_DOUBLE, index, hash1(ClassFile.TAG_DOUBLE, Double.hashCode(d)));
            val = d;
        }

        @Override
        void writeTo(BufWriterImpl pool) {
            pool.writeU1(tag);
            pool.writeDouble(val);
        }

        @Override
        public DoubleEntry clone(ConstantPoolBuilder cp) {
            return cp.canWriteDirect(constantPool) ? this : cp.doubleEntry(val);
        }

        @Override
        public double doubleValue() {
            return val;
        }

        @Override
        public ConstantDesc constantValue() {
            return val;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o instanceof DoubleEntryImpl e) {
                return doubleValue() == e.doubleValue();
            }
            return false;
        }
    }

    static class CpException extends RuntimeException {
        static final long serialVersionUID = 32L;

        CpException(String s) {
            super(s);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy