001package serp.bytecode.lowlevel;
002
003import java.io.DataInput;
004import java.io.DataOutput;
005import java.io.IOException;
006
007import serp.bytecode.visitor.VisitAcceptor;
008
009/**
010 * Base type for all constant pool entries. Entries should generally be
011 * considered immutable; modifying an entry directly can have dire
012 * consequences, and often renders the resulting class file invalid.
013 *
014 * <p>Entries cannot be shared among constant pools.</p>
015 *
016 * @author Abe White
017 */
018public abstract class Entry implements VisitAcceptor {
019    public static final int UTF8 = 1;
020    public static final int INT = 3;
021    public static final int FLOAT = 4;
022    public static final int LONG = 5;
023    public static final int DOUBLE = 6;
024    public static final int CLASS = 7;
025    public static final int STRING = 8;
026    public static final int FIELD = 9;
027    public static final int METHOD = 10;
028    public static final int INTERFACEMETHOD = 11;
029    public static final int NAMEANDTYPE = 12;
030    public static final int METHODHANDLE = 15;
031    public static final int METHODTYPE = 16;
032    public static final int INVOKEDYNAMIC = 18;
033    
034    private ConstantPool _pool = null;
035    private int _index = 0;
036
037    /**
038     * Read a single entry from the given bytecode stream and returns it.
039     */
040    public static Entry read(DataInput in) throws IOException {
041        Entry entry = create(in.readUnsignedByte());
042        entry.readData(in);
043        return entry;
044    }
045
046    /**
047     * Write the given entry to the given bytecode stream.
048     */
049    public static void write(Entry entry, DataOutput out)
050        throws IOException {
051        out.writeByte(entry.getType());
052        entry.writeData(out);
053    }
054
055    /**
056     * Create an entry based on its type code.
057     */
058    public static Entry create(int type) {
059        switch (type) {
060        case CLASS:
061            return new ClassEntry();
062        case FIELD:
063            return new FieldEntry();
064        case METHOD:
065            return new MethodEntry();
066        case INTERFACEMETHOD:
067            return new InterfaceMethodEntry();
068        case STRING:
069            return new StringEntry();
070        case INT:
071            return new IntEntry();
072        case FLOAT:
073            return new FloatEntry();
074        case LONG:
075            return new LongEntry();
076        case DOUBLE:
077            return new DoubleEntry();
078        case NAMEANDTYPE:
079            return new NameAndTypeEntry();
080        case UTF8:
081            return new UTF8Entry();
082        case METHODHANDLE:
083                return new MethodHandleEntry();
084        case METHODTYPE:
085                return new MethodTypeEntry();
086        case INVOKEDYNAMIC:
087                return new InvokeDynamicEntry();
088        default:
089            throw new IllegalArgumentException("type = " + type);
090        }
091    }
092
093    /**
094     * Return the type code for this entry type.
095     */
096    public abstract int getType();
097
098    /**
099     * Return true if this is a wide entry -- i.e. if it takes up two
100     * places in the constant pool. Returns false by default.
101     */
102    public boolean isWide() {
103        return false;
104    }
105
106    /**
107     * Returns the constant pool containing this entry, or null if none.
108     */
109    public ConstantPool getPool() {
110        return _pool;
111    }
112
113    /**
114     * Returns the index of the entry in the owning constant pool, or 0.
115     */
116    public int getIndex() {
117        return _index;
118    }
119
120    /**
121     * This method is called after reading the entry type from bytecode.
122     * It should read all the data for this entry from the given stream.
123     */
124    abstract void readData(DataInput in) throws IOException;
125
126    /**
127     * This method is called after writing the entry type to bytecode.
128     * It should write all data for this entry to the given stream.
129     */
130    abstract void writeData(DataOutput out) throws IOException;
131
132    /**
133     * Subclasses must call this method before their state is mutated.
134     */
135    Object beforeModify() {
136        if (_pool == null)
137            return null;
138        return _pool.getKey(this);
139    }
140
141    /**
142     * Subclasses must call this method when their state is mutated.
143     */
144    void afterModify(Object key) {
145        if (_pool != null)
146            _pool.modifyEntry(key, this);
147    }
148
149    /**
150     * Sets the owning pool of the entry.
151     */
152    void setPool(ConstantPool pool) {
153        // attempting to overwrite current pool?
154        if (_pool != null && pool != null && _pool != pool)
155            throw new IllegalStateException("Entry already belongs to a pool");
156        _pool = pool;
157    }
158
159    /**
160     * Set the index of this entry within the pool.
161     */
162    void setIndex(int index) {
163        _index = index;
164    }
165}