001    package edu.rice.cs.cunit.classFile.code.instructions;
002    
003    import edu.rice.cs.cunit.classFile.code.Opcode;
004    import edu.rice.cs.cunit.util.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 int getBytecodeLength(int 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, int pc, int 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(int 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            final BranchInstruction branchInstruction = (BranchInstruction)o;
123    
124            if (_opcode != branchInstruction._opcode) {
125                return false;
126            }
127            if (_target != branchInstruction._target) {
128                return false;
129            }
130    
131            return true;
132        }
133    
134        /**
135         * Return hash code.
136         *
137         * @return hash code
138         */
139        public int hashCode() {
140            int result;
141            result = (int)_opcode;
142            result = 29 * result + _target;
143            return result;
144        }
145    
146        /**
147         * Return an array of target indices.
148         *
149         * @return array of target indices.
150         */
151        public int[] getBranchTargets() {
152            return new int[]{_target};
153        }
154    
155        /**
156         * Set the branch target indices.
157         *
158         * @param branchTargets array of target indices
159         */
160        public void setBranchTargets(int[] branchTargets) {
161            if (branchTargets.length != 1) {
162                throw new IllegalArgumentException("Branch instruction can only have one target");
163            }
164            _target = branchTargets[0];
165        }
166    
167        /**
168         * Return instruction in human-readable form.
169         *
170         * @return string representation
171         */
172        public String toString() {
173            StringBuilder x = new StringBuilder();
174            x.append(Opcode.getOpcodeName(_opcode));
175            x.append(" ");
176            x.append(_target);
177            return x.toString();
178        }
179    }
180