001package serp.bytecode;
002
003import java.io.*;
004import java.util.*;
005
006import serp.bytecode.visitor.*;
007
008/**
009 * An instruction that specifies a position in the code block to jump to.
010 * Examples include <code>go2, jsr</code>, etc.
011 *
012 * @author Abe White
013 */
014public abstract class JumpInstruction extends Instruction 
015    implements InstructionPtr {
016    private InstructionPtrStrategy _target = new InstructionPtrStrategy(this);
017
018    JumpInstruction(Code owner, int opcode) {
019        super(owner, opcode);
020    }
021
022    /**
023     * Get the current target instruction to jump to, if it has been set.
024     */
025    public Instruction getTarget() {
026        return _target.getTargetInstruction();
027    }
028
029    /**
030     * Set the instruction to jump to; the instruction must already be
031     * added to the code block.
032     *
033     * @return this instruction, for method chaining
034     */
035    public JumpInstruction setTarget(Instruction instruction) {
036        _target.setTargetInstruction(instruction);
037        return this;
038    }
039
040    /**
041     * JumpInstructions are equal if they represent the same operation and
042     * the instruction they jump to is the
043     * same, or if the jump Instruction of either is unset.
044     */
045    public boolean equalsInstruction(Instruction other) {
046        if (this == other)
047            return true;
048        if (!super.equalsInstruction(other))
049            return false;
050
051        Instruction target = ((JumpInstruction) other).getTarget();
052        return target == null || getTarget() == null || target == getTarget();
053    }
054
055    public void updateTargets() {
056        _target.updateTargets();
057    }
058
059    public void replaceTarget(Instruction oldTarget, Instruction newTarget) {
060        _target.replaceTarget(oldTarget, newTarget);
061    }
062
063    public void acceptVisit(BCVisitor visit) {
064        visit.enterJumpInstruction(this);
065        visit.exitJumpInstruction(this);
066    }
067
068    void read(Instruction orig) {
069        super.read(orig);
070        _target.setByteIndex(((JumpInstruction) orig)._target.getByteIndex());
071    }
072
073    void read(DataInput in) throws IOException {
074        super.read(in);
075        switch (getOpcode()) {
076        case Constants.GOTOW:
077        case Constants.JSRW:
078            _target.setByteIndex(getByteIndex() + in.readInt());
079            break;
080        default:
081            _target.setByteIndex(getByteIndex() + in.readShort());
082        }
083    }
084
085    void write(DataOutput out) throws IOException {
086        super.write(out);
087        switch (getOpcode()) {
088        case Constants.GOTOW:
089        case Constants.JSRW:
090            out.writeInt(_target.getByteIndex() - getByteIndex());
091            break;
092        default:
093            out.writeShort(_target.getByteIndex() - getByteIndex());
094        }
095    }
096
097    public void setOffset(int offset) {
098        _target.setByteIndex(getByteIndex() + offset);
099    }
100
101    public int getOffset() {
102        return _target.getByteIndex() - getByteIndex();
103    }
104}