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     * Wide branch Java instruction.
008     *
009     * @author Mathias Ricken
010     */
011    public class WideBranchInstruction 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 WideBranchInstruction(byte opcode, int target) {
029            switch(opcode) {
030                case Opcode.GOTO_W:
031                case Opcode.JSR_W:
032                    break;
033                default:
034                    throw new IllegalArgumentException("Invalid wide branch opcode");
035            }
036            _opcode = opcode;
037            _target = target;
038        }
039    
040        /**
041         * Get the opcode of this instruction.
042         *
043         * @return opcode
044         */
045        public byte getOpcode() {
046            return _opcode;
047        }
048    
049        /**
050         * Get the length bytecode for this instruction, padded for the specified program counter value.
051         *
052         * @param pc PC for padding
053         *
054         * @return bytecode length
055         */
056        public int getBytecodeLength(int pc) {
057            return 5;
058        }
059    
060        /**
061         * Make a new wide branch instruction from the bytecode stating at pc, padded using paddingPC, and use the line
062         * number table for branches.
063         *
064         * @param bytecode  bytecode
065         * @param pc        starting index in bytecode
066         * @param paddingPC PC for padding
067         * @param lnt       line number table for branches
068         */
069        public WideBranchInstruction(byte[] bytecode, int pc, int paddingPC, LineNumberTable lnt) {
070            _opcode = bytecode[pc];
071            _target = lnt.getLineNumber(pc + Types.intFromBytes(bytecode, pc + 1));
072        }
073    
074        /**
075         * Get the bytecode for this instruction, padded for the specified program counter value, with branch targets
076         * according to the specified line number table
077         *
078         * @param pc  PC for padding
079         * @param lnt line number table for branches
080         *
081         * @return bytecode
082         */
083        public byte[] getBytecode(int pc, LineNumberTable lnt) {
084            byte[] b = new byte[getBytecodeLength(pc)];
085    
086            b[0] = getOpcode();
087            Types.bytesFromInt(lnt.getPC(_target) - pc, b, 1);
088    
089            return b;
090        }
091    
092        /**
093         * Return true of this instruction and the other object are equal.
094         *
095         * @param o other object
096         *
097         * @return true if equal
098         */
099        public boolean equals(Object o) {
100            if (this == o) {
101                return true;
102            }
103            if (!(o instanceof WideBranchInstruction)) {
104                return false;
105            }
106    
107            final WideBranchInstruction wideBranchInstruction = (WideBranchInstruction)o;
108    
109            if (_opcode != wideBranchInstruction._opcode) {
110                return false;
111            }
112            if (_target != wideBranchInstruction._target) {
113                return false;
114            }
115    
116            return true;
117        }
118    
119        /**
120         * Return hash code.
121         *
122         * @return hash code
123         */
124        public int hashCode() {
125            int result;
126            result = (int)_opcode;
127            result = 29 * result + _target;
128            return result;
129        }
130    
131        /**
132         * Return an array of target indices.
133         *
134         * @return array of target indices.
135         */
136        public int[] getBranchTargets() {
137            return new int[]{
138                _target
139            };
140        }
141    
142        /**
143         * Set the branch target indices.
144         *
145         * @param branchTargets array of target indices
146         */
147        public void setBranchTargets(int[] branchTargets) {
148            if (branchTargets.length != 1) {
149                throw new IllegalArgumentException("Wide branch instruction can only have one target");
150            }
151            _target = branchTargets[0];
152        }
153    
154    
155        /**
156         * Return instruction in human-readable form.
157         *
158         * @return string representation
159         */
160        public String toString() {
161            StringBuilder x = new StringBuilder();
162            x.append(Opcode.getOpcodeName(_opcode));
163            x.append(" ");
164            x.append(_target);
165            return x.toString();
166        }
167    }
168