serp.bytecode.Local Maven / Gradle / Ivy
package serp.bytecode;
import java.io.*;
import java.util.*;
import serp.bytecode.lowlevel.*;
import serp.bytecode.visitor.*;
import serp.util.*;
/**
* A local variable or local variable type.
*
* @author Abe White
* @author Sakir Murat Cengiz
*/
public abstract class Local implements BCEntity, InstructionPtr {
private LocalTable _owner = null;
private InstructionPtrStrategy _target = new InstructionPtrStrategy(this);
private Instruction _end = null;
private int _length = 0;
private int _nameIndex = 0;
private int _descriptorIndex = 0;
private int _index = 0;
Local(LocalTable owner) {
_owner = owner;
}
/**
* The owning table.
*/
public LocalTable getTable() {
return _owner;
}
void invalidate() {
_owner = null;
}
//////////////////////////
// Local index operations
//////////////////////////
/**
* Get the local variable index of the current frame for this local.
*/
public int getLocal() {
return _index;
}
/**
* Set the local variable index of the current frame for this local.
*/
public void setLocal(int index) {
_index = index;
}
/**
* Return the parameter that this local corresponds to, or -1 if none.
*/
public int getParam() {
return getCode().getParamsIndex(getLocal());
}
/**
* Set the method parameter that this local corresponds to.
*/
public void setParam(int param) {
setLocal(_owner.getCode().getLocalsIndex(param));
}
////////////////////////////
// Start, Length operations
////////////////////////////
/**
* Return the index into the code byte array at which this local starts.
*/
public int getStartPc() {
return _target.getByteIndex();
}
/**
* Return the instruction marking the beginning of this local.
*/
public Instruction getStart() {
return _target.getTargetInstruction();
}
/**
* Set the index into the code byte array at which this local starts.
*/
public void setStartPc(int startPc) {
_target.setByteIndex(startPc);
}
/**
* Set the {@link Instruction} marking the beginning this local.
* The instruction must already be a part of the method.
* WARNING: if this instruction is deleted, the results are undefined.
*/
public void setStart(Instruction instruction) {
_target.setTargetInstruction(instruction);
}
/**
* The last {@link Instruction} for which this local is in scope.
*/
public Instruction getEnd() {
if (_end != null)
return _end;
int idx = _target.getByteIndex() + _length;
Instruction end = getCode().getInstruction(idx);
if (end != null && (end.prev instanceof Instruction)) {
return (Instruction) end.prev;
}
return getCode().getLastInstruction();
}
/**
* Get the number of bytes for which this local has a value in
* the code byte array.
*/
public int getLength() {
if (_end != null)
return _end.getByteIndex() + _end.getLength()
- _target.getByteIndex();
return _length;
}
/**
* Set the last {@link Instruction} for which this local is in scope.
* The instruction must already be a part of the method.
* WARNING: if this instruction is deleted, the results are undefined.
*/
public void setEnd(Instruction end) {
if (end.getCode() != getCode())
throw new IllegalArgumentException("Instruction pointers and "
+ "targets must be part of the same code block.");
_end = end;
_length = -1;
}
/**
* Set the number of bytes for which this local has a value in
* the code byte array.
*/
public void setLength(int length) {
if (length < 0)
throw new IllegalArgumentException(String.valueOf(length));
_length = length;
_end = null;
}
public void updateTargets() {
_target.updateTargets();
_end = getEnd();
}
public void replaceTarget(Instruction oldTarget, Instruction newTarget) {
_target.replaceTarget(oldTarget, newTarget);
if (getEnd() == oldTarget)
setEnd(newTarget);
}
/////////////////////////
// Name, Type operations
/////////////////////////
/**
* Return the {@link ConstantPool} index of the {@link UTF8Entry} that
* describes the name of this local. Defaults to 0.
*/
public int getNameIndex() {
return _nameIndex;
}
/**
* Set the {@link ConstantPool} index of the {@link UTF8Entry} that
* describes the name of this local.
*/
public void setNameIndex(int nameIndex) {
_nameIndex = nameIndex;
}
/**
* Return the name of this local, or null if unset.
*/
public String getName() {
if (getNameIndex() == 0)
return null;
return ((UTF8Entry) getPool().getEntry(getNameIndex())).getValue();
}
/**
* Set the name of this inner local.
*/
public void setName(String name) {
if (name == null)
setNameIndex(0);
else
setNameIndex(getPool().findUTF8Entry(name, true));
}
/**
* Return the {@link ConstantPool} index of the {@link UTF8Entry} that
* describes this local. Defaults to 0.
*/
public int getTypeIndex() {
return _descriptorIndex;
}
/**
* Set the {@link ConstantPool} index of the {@link UTF8Entry} that
* describes this local.
*/
public void setTypeIndex(int index) {
_descriptorIndex = index;
}
/**
* Return the full name of the local's type, or null if unset.
*/
public String getTypeName() {
if (getTypeIndex() == 0)
return null;
UTF8Entry entry = (UTF8Entry) getPool().getEntry(getTypeIndex());
return getProject().getNameCache().getExternalForm(entry.getValue(),
false);
}
/**
* Set the type of this local.
*/
public void setType(String type) {
if (type == null)
setTypeIndex(0);
else {
type = getProject().getNameCache().getInternalForm(type, true);
setTypeIndex(getPool().findUTF8Entry(type, true));
}
}
///////////////////////////
// BCEntity implementation
///////////////////////////
public Project getProject() {
return _owner.getProject();
}
public ConstantPool getPool() {
return _owner.getPool();
}
public ClassLoader getClassLoader() {
return _owner.getClassLoader();
}
public boolean isValid() {
return _owner != null;
}
//////////////////
// I/O operations
//////////////////
void read(DataInput in) throws IOException {
setStartPc(in.readUnsignedShort());
setLength(in.readUnsignedShort());
setNameIndex(in.readUnsignedShort());
setTypeIndex(in.readUnsignedShort());
setLocal(in.readUnsignedShort());
}
void write(DataOutput out) throws IOException {
out.writeShort(getStartPc());
out.writeShort(getLength());
out.writeShort(getNameIndex());
out.writeShort(getTypeIndex());
out.writeShort(getLocal());
}
public Code getCode() {
return _owner.getCode();
}
}