serp.bytecode.SwitchInstruction Maven / Gradle / Ivy
package serp.bytecode;
import java.io.*;
import java.util.*;
/**
* Contains functionality common to the different switch types
* (TableSwitch and LookupSwitch).
*
* @author Eric Lindauer
*/
public abstract class SwitchInstruction extends JumpInstruction {
private List _cases = new LinkedList();
public SwitchInstruction(Code owner, int opcode) {
super(owner, opcode);
}
/**
* Returns the current byte offsets for the different
* switch cases in this Instruction.
*/
public int[] getOffsets() {
int bi = getByteIndex();
int[] offsets = new int[_cases.size()];
for (int i = 0; i < offsets.length; i++)
offsets[i] = ((InstructionPtrStrategy) _cases.get(i)).getByteIndex()
- bi;
return offsets;
}
/**
* Sets the offsets for the instructions representing the different
* switch statement cases. WARNING: these offsets will not be changed
* in the event that the code is modified following this call. It is
* typically a good idea to follow this call with a call to updateTargets
* as soon as the instructions at the given offsets are valid, at which
* point the Instructions themselves will be used as the targets and the
* offsets will be updated as expected.
*/
public void setOffsets(int[] offsets) {
int bi = getByteIndex();
_cases.clear();
for (int i = 0; i < offsets.length; i++) {
InstructionPtrStrategy next = new InstructionPtrStrategy(this);
next.setByteIndex(offsets[i] + bi);
_cases.add(next);
}
}
public int countTargets() {
return _cases.size();
}
int getLength() {
// don't call super.getLength(), cause JumpInstruction will return
// value assuming this is an 'if' or 'goto' instruction
int length = 1;
// make the first byte of the 'default' a multiple of 4 from the
// start of the method
int byteIndex = getByteIndex() + 1;
for (; (byteIndex % 4) != 0; byteIndex++, length++);
return length;
}
/**
* Synonymous with {@link #getTarget}.
*/
public Instruction getDefaultTarget() {
return getTarget();
}
/**
* Synonymous with {@link #getOffset}.
*/
public int getDefaultOffset() {
return getOffset();
}
/**
* Synonymous with {@link #setOffset}.
*/
public SwitchInstruction setDefaultOffset(int offset) {
setOffset(offset);
return this;
}
/**
* Synonymous with {@link #setTarget}.
*/
public SwitchInstruction setDefaultTarget(Instruction ins) {
return (SwitchInstruction) setTarget(ins);
}
/**
* Return the targets for this switch, or empty array if not set.
*/
public Instruction[] getTargets() {
Instruction[] result = new Instruction[_cases.size()];
for (int i = 0; i < _cases.size(); i++)
result[i] = ((InstructionPtrStrategy) _cases.get(i)).
getTargetInstruction();
return result;
}
/**
* Set the jump points for this switch.
*
* @return this instruction, for method chaining
*/
public SwitchInstruction setTargets(Instruction[] targets) {
_cases.clear();
if (targets != null)
for (int i = 0; i < targets.length; i++)
addTarget(targets[i]);
return this;
}
/**
* Add a target to this switch.
*
* @return this instruction, for method chaining
*/
public SwitchInstruction addTarget(Instruction target) {
_cases.add(new InstructionPtrStrategy(this, target));
return this;
}
public int getStackChange() {
return -1;
}
public void updateTargets() {
super.updateTargets();
for (Iterator itr = _cases.iterator(); itr.hasNext();)
((InstructionPtrStrategy) itr.next()).updateTargets();
}
public void replaceTarget(Instruction oldTarget, Instruction newTarget) {
super.replaceTarget(oldTarget, newTarget);
for (Iterator itr = _cases.iterator(); itr.hasNext();)
((InstructionPtrStrategy) itr.next()).replaceTarget(oldTarget,
newTarget);
}
void read(Instruction orig) {
super.read(orig);
SwitchInstruction ins = (SwitchInstruction) orig;
_cases.clear();
InstructionPtrStrategy incoming;
for (Iterator itr = ins._cases.iterator(); itr.hasNext();) {
incoming = (InstructionPtrStrategy) itr.next();
InstructionPtrStrategy next = new InstructionPtrStrategy(this);
next.setByteIndex(incoming.getByteIndex());
_cases.add(next);
}
}
void clearTargets() {
_cases.clear();
}
void readTarget(DataInput in) throws IOException {
InstructionPtrStrategy next = new InstructionPtrStrategy(this);
next.setByteIndex(getByteIndex() + in.readInt());
_cases.add(next);
}
/**
* Set the match-jumppt pairs for this switch.
*
* @return this instruction, for method chaining
*/
public SwitchInstruction setCases(int[] matches, Instruction[] targets) {
setMatches(matches);
setTargets(targets);
return this;
}
public SwitchInstruction setMatches(int[] matches) {
clearMatches();
for (int i = 0; i < matches.length; i++)
addMatch(matches[i]);
return this;
}
/**
* Add a case to this switch.
*
* @return this instruction, for method chaining
*/
public SwitchInstruction addCase(int match, Instruction target) {
addMatch(match);
addTarget(target);
return this;
}
public abstract SwitchInstruction addMatch(int match);
public abstract int[] getMatches();
abstract void clearMatches();
void calculateOpcode() {
}
}