001    package serp.bytecode.lowlevel;
002    
003    import java.io.DataInput;
004    import java.io.DataOutput;
005    import java.io.IOException;
006    
007    import 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     */
018    public 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    }