001    package sysModel.classFile.code.instructions;
002    
003    import sysModel.classFile.code.Opcode;
004    import sysModel.classFile.Types;
005    
006    /**
007     * Branch Java instruction.
008     *
009     * @author Mathias Ricken
010     */
011    public class BranchInstruction extends AInstruction {
012        /**
013         * Opcode.
014         */
015        protected byte _opcode;
016    
017        /**
018         * Branch target.
019         */
020        protected int _target;
021    
022        /**
023         * Constructor.
024         *
025         * @param opcode branch opcode
026         * @param target target line number
027         */
028        public BranchInstruction(byte opcode, int target) {
029            switch(opcode) {
030                case Opcode.IFEQ:
031                case Opcode.IFNE:
032                case Opcode.IFLT:
033                case Opcode.IFGE:
034                case Opcode.IFGT:
035                case Opcode.IFLE:
036                case Opcode.IF_ICMPEQ:
037                case Opcode.IF_ICMPNE:
038                case Opcode.IF_ICMPLT:
039                case Opcode.IF_ICMPGE:
040                case Opcode.IF_ICMPGT:
041                case Opcode.IF_ICMPLE:
042                case Opcode.IF_ACMPEQ:
043                case Opcode.IF_ACMPNE:
044                case Opcode.GOTO:
045                case Opcode.JSR:
046                case Opcode.IFNULL:
047                case Opcode.IFNONNULL:
048                    break;
049                default:
050                    throw new IllegalArgumentException("Invalid branch opcode");
051            }
052            _opcode = opcode;
053            _target = target;
054        }
055    
056        /**
057         * Get the opcode of this instruction.
058         *
059         * @return opcode
060         */
061        public byte getOpcode() {
062            return _opcode;
063        }
064    
065        /**
066         * Get the length bytecode for this instruction, padded for the specified program counter value.
067         *
068         * @param pc PC for padding
069         *
070         * @return bytecode length
071         */
072        public short getBytecodeLength(short pc) {
073            return 3;
074        }
075    
076        /**
077         * Make a new branch instruction from the bytecode stating at pc, padded using paddingPC, and use the line number
078         * table for branches.
079         *
080         * @param bytecode  bytecode
081         * @param pc        starting index in bytecode
082         * @param paddingPC PC for padding
083         * @param lnt       line number table for branches
084         */
085        public BranchInstruction(byte[] bytecode, short pc, short paddingPC, LineNumberTable lnt) {
086            _opcode = bytecode[pc];
087            _target = lnt.getLineNumber(pc + Types.shortFromBytes(bytecode, pc + 1));
088        }
089    
090        /**
091         * Get the bytecode for this instruction, padded for the specified program counter value, with branch targets
092         * according to the specified line number table * @param pc  PC for padding
093         *
094         * @param lnt line number table for branches
095         *
096         * @return bytecode
097         */
098        public byte[] getBytecode(short pc, LineNumberTable lnt) {
099            byte[] b = new byte[getBytecodeLength(pc)];
100    
101            b[0] = getOpcode();
102            Types.bytesFromShort((short)(lnt.getPC(_target) - pc), b, 1);
103    
104            return b;
105        }
106    
107        /**
108         * Return true of this instruction and the other object are equal.
109         *
110         * @param o other object
111         *
112         * @return true if equal
113         */
114        public boolean equals(Object o) {
115            if (this == o) {
116                return true;
117            }
118            if (!(o instanceof BranchInstruction)) {
119                return false;
120            }
121    
122            BranchInstruction branchInstruction = (BranchInstruction)o;
123    
124            if (_opcode != branchInstruction._opcode) {
125                return false;
126            }
127            return _target == branchInstruction._target;
128        }
129    
130        /**
131         * Return hash code.
132         *
133         * @return hash code
134         */
135        public int hashCode() {
136            int result;
137            result = (int)_opcode;
138            result = 29 * result + _target;
139            return result;
140        }
141    
142        /**
143         * Return an array of target indices.
144         *
145         * @return array of target indices.
146         */
147        public short[] getBranchTargets() {
148            return new short[]{(short)_target};
149        }
150    
151        /**
152         * Set the branch target indices.
153         *
154         * @param branchTargets array of target indices
155         */
156        public void setBranchTargets(short[] branchTargets) {
157            if (1 != branchTargets.length) {
158                throw new IllegalArgumentException("Branch instruction can only have one target");
159            }
160            _target = branchTargets[0];
161        }
162    
163        /**
164         * Return instruction in human-readable form.
165         *
166         * @return string representation
167         */
168        public String toString() {
169            StringBuffer x = new StringBuffer();
170            x.append(Opcode.getOpcodeName(_opcode));
171            x.append(' ');
172            x.append(_target);
173            return x.toString();
174        }
175    }
176