serp.bytecode.ExceptionHandler Maven / Gradle / Ivy
package serp.bytecode;
import java.io.*;
import java.util.*;
import serp.bytecode.lowlevel.*;
import serp.bytecode.visitor.*;
import serp.util.*;
/**
* Represents a try {} catch() {}
statement in bytecode.
*
* @author Abe White
*/
public class ExceptionHandler implements InstructionPtr, BCEntity,
VisitAcceptor {
private int _catchIndex = 0;
private Code _owner = null;
private InstructionPtrStrategy _tryStart = new InstructionPtrStrategy(this);
private InstructionPtrStrategy _tryEnd = new InstructionPtrStrategy(this);
private InstructionPtrStrategy _tryHandler = new InstructionPtrStrategy
(this);
ExceptionHandler(Code owner) {
_owner = owner;
}
/**
* Return the owning code block.
*/
public Code getCode() {
return _owner;
}
///////////////////
// Body operations
///////////////////
/**
* Return the instruction marking the beginning of the try {} block.
*/
public Instruction getTryStart() {
return _tryStart.getTargetInstruction();
}
/**
* Set the {@link Instruction} marking the beginning of the try block.
* The instruction must already be a part of the method.
*/
public void setTryStart(Instruction instruction) {
_tryStart.setTargetInstruction(instruction);
}
/**
* Return the instruction at the end of the try {} block.
*/
public Instruction getTryEnd() {
return _tryEnd.getTargetInstruction();
}
/**
* Set the Instruction at the end of the try block. The
* Instruction must already be a part of the method.
*/
public void setTryEnd(Instruction instruction) {
_tryEnd.setTargetInstruction(instruction);
}
//////////////////////
// Handler operations
//////////////////////
/**
* Return the instruction marking the beginning of the catch {} block.
*/
public Instruction getHandlerStart() {
return _tryHandler.getTargetInstruction();
}
/**
* Set the {@link Instruction} marking the beginning of the catch block.
* The instruction must already be a part of the method.
* WARNING: if this instruction is deleted, the results are undefined.
*/
public void setHandlerStart(Instruction instruction) {
_tryHandler.setTargetInstruction(instruction);
}
////////////////////
// Catch operations
////////////////////
/**
* Return the index into the class {@link ConstantPool} of the
* {@link ClassEntry} describing the exception type this handler catches.
*/
public int getCatchIndex() {
return _catchIndex;
}
/**
* Set the index into the class {@link ConstantPool} of the
* {@link ClassEntry} describing the exception type this handler catches.
*/
public void setCatchIndex(int catchTypeIndex) {
_catchIndex = catchTypeIndex;
}
/**
* Return the name of the exception type; returns null for catch-all
* clauses used to implement finally blocks. The name will be returned
* in a forum suitable for a {@link Class#forName} call.
*/
public String getCatchName() {
if (_catchIndex == 0)
return null;
ClassEntry entry = (ClassEntry) getPool().getEntry(_catchIndex);
return getProject().getNameCache().getExternalForm(entry.getNameEntry().
getValue(), false);
}
/**
* Return the {@link Class} of the exception type; returns null for
* catch-all clauses used to implement finally blocks.
*/
public Class getCatchType() {
String name = getCatchName();
if (name == null)
return null;
return Strings.toClass(name, getClassLoader());
}
/**
* Return the bytecode of the exception type; returns null for
* catch-all clauses used to implement finally blocks.
*/
public BCClass getCatchBC() {
String name = getCatchName();
if (name == null)
return null;
return getProject().loadClass(name, getClassLoader());
}
/**
* Set the class of the exception type, or null for catch-all clauses used
* with finally blocks.
*/
public void setCatch(String name) {
if (name == null)
_catchIndex = 0;
else
_catchIndex = getPool().findClassEntry(getProject().getNameCache().
getInternalForm(name, false), true);
}
/**
* Set the class of the exception type, or null for catch-all clauses used
* for finally blocks.
*/
public void setCatch(Class type) {
if (type == null)
setCatch((String) null);
else
setCatch(type.getName());
}
/**
* Set the class of the exception type, or null for catch-all clauses used
* for finally blocks.
*/
public void setCatch(BCClass type) {
if (type == null)
setCatch((String) null);
else
setCatch(type.getName());
}
/////////////////////////////////
// InstructionPtr implementation
/////////////////////////////////
public void updateTargets() {
_tryStart.updateTargets();
_tryEnd.updateTargets();
_tryHandler.updateTargets();
}
public void replaceTarget(Instruction oldTarget, Instruction newTarget) {
_tryStart.replaceTarget(oldTarget, newTarget);
_tryEnd.replaceTarget(oldTarget, newTarget);
_tryHandler.replaceTarget(oldTarget, newTarget);
}
///////////////////////////
// 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;
}
////////////////////////////////
// VisitAcceptor implementation
////////////////////////////////
public void acceptVisit(BCVisitor visit) {
visit.enterExceptionHandler(this);
visit.exitExceptionHandler(this);
}
//////////////////
// I/O operations
//////////////////
void read(ExceptionHandler orig) {
_tryStart.setByteIndex(orig._tryStart.getByteIndex());
_tryEnd.setByteIndex(orig._tryEnd.getByteIndex());
_tryHandler.setByteIndex(orig._tryHandler.getByteIndex());
// done at a high level so that if the name isn't in our constant pool,
// it will be added
setCatch(orig.getCatchName());
}
void read(DataInput in) throws IOException {
setTryStart(in.readUnsignedShort());
setTryEnd(in.readUnsignedShort());
setHandlerStart(in.readUnsignedShort());
setCatchIndex(in.readUnsignedShort());
}
void write(DataOutput out) throws IOException {
out.writeShort(getTryStartPc());
out.writeShort(getTryEndPc());
out.writeShort(getHandlerStartPc());
out.writeShort(getCatchIndex());
}
public void setTryStart(int start) {
_tryStart.setByteIndex(start);
}
public int getTryStartPc() {
return _tryStart.getByteIndex();
}
public void setTryEnd(int end) {
setTryEnd((Instruction) _owner.getInstruction(end).prev);
}
/**
* Return the program counter end position for this exception handler.
* This represents an index into the code byte array.
*/
public int getTryEndPc() {
return _tryEnd.getByteIndex() + getTryEnd().getLength();
}
public void setHandlerStart(int handler) {
_tryHandler.setByteIndex(handler);
}
public int getHandlerStartPc() {
return _tryHandler.getByteIndex();
}
void invalidate() {
_owner = null;
}
}