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 }