001    package serp.bytecode;
002    
003    import java.io.*;
004    import java.util.*;
005    
006    import 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     */
014    public 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    }