serp.bytecode.lowlevel.Entry Maven / Gradle / Ivy
package serp.bytecode.lowlevel;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import serp.bytecode.visitor.VisitAcceptor;
/**
* Base type for all constant pool entries. Entries should generally be
* considered immutable; modifying an entry directly can have dire
* consequences, and often renders the resulting class file invalid.
*
* Entries cannot be shared among constant pools.
*
* @author Abe White
*/
public abstract class Entry implements VisitAcceptor {
public static final int UTF8 = 1;
public static final int INT = 3;
public static final int FLOAT = 4;
public static final int LONG = 5;
public static final int DOUBLE = 6;
public static final int CLASS = 7;
public static final int STRING = 8;
public static final int FIELD = 9;
public static final int METHOD = 10;
public static final int INTERFACEMETHOD = 11;
public static final int NAMEANDTYPE = 12;
public static final int METHODHANDLE = 15;
public static final int METHODTYPE = 16;
public static final int INVOKEDYNAMIC = 18;
private ConstantPool _pool = null;
private int _index = 0;
/**
* Read a single entry from the given bytecode stream and returns it.
*/
public static Entry read(DataInput in) throws IOException {
Entry entry = create(in.readUnsignedByte());
entry.readData(in);
return entry;
}
/**
* Write the given entry to the given bytecode stream.
*/
public static void write(Entry entry, DataOutput out)
throws IOException {
out.writeByte(entry.getType());
entry.writeData(out);
}
/**
* Create an entry based on its type code.
*/
public static Entry create(int type) {
switch (type) {
case CLASS:
return new ClassEntry();
case FIELD:
return new FieldEntry();
case METHOD:
return new MethodEntry();
case INTERFACEMETHOD:
return new InterfaceMethodEntry();
case STRING:
return new StringEntry();
case INT:
return new IntEntry();
case FLOAT:
return new FloatEntry();
case LONG:
return new LongEntry();
case DOUBLE:
return new DoubleEntry();
case NAMEANDTYPE:
return new NameAndTypeEntry();
case UTF8:
return new UTF8Entry();
case METHODHANDLE:
return new MethodHandleEntry();
case METHODTYPE:
return new MethodTypeEntry();
case INVOKEDYNAMIC:
return new InvokeDynamicEntry();
default:
throw new IllegalArgumentException("type = " + type);
}
}
/**
* Return the type code for this entry type.
*/
public abstract int getType();
/**
* Return true if this is a wide entry -- i.e. if it takes up two
* places in the constant pool. Returns false by default.
*/
public boolean isWide() {
return false;
}
/**
* Returns the constant pool containing this entry, or null if none.
*/
public ConstantPool getPool() {
return _pool;
}
/**
* Returns the index of the entry in the owning constant pool, or 0.
*/
public int getIndex() {
return _index;
}
/**
* This method is called after reading the entry type from bytecode.
* It should read all the data for this entry from the given stream.
*/
abstract void readData(DataInput in) throws IOException;
/**
* This method is called after writing the entry type to bytecode.
* It should write all data for this entry to the given stream.
*/
abstract void writeData(DataOutput out) throws IOException;
/**
* Subclasses must call this method before their state is mutated.
*/
Object beforeModify() {
if (_pool == null)
return null;
return _pool.getKey(this);
}
/**
* Subclasses must call this method when their state is mutated.
*/
void afterModify(Object key) {
if (_pool != null)
_pool.modifyEntry(key, this);
}
/**
* Sets the owning pool of the entry.
*/
void setPool(ConstantPool pool) {
// attempting to overwrite current pool?
if (_pool != null && pool != null && _pool != pool)
throw new IllegalStateException("Entry already belongs to a pool");
_pool = pool;
}
/**
* Set the index of this entry within the pool.
*/
void setIndex(int index) {
_index = index;
}
}