001    package sysModel.classFile.code.instructions;
002    
003    import sysModel.classFile.code.Opcode;
004    import sysModel.classFile.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 short getBytecodeLength(short 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, short pc, short 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(short 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            WideBranchInstruction wideBranchInstruction = (WideBranchInstruction)o;
108    
109            if (_opcode != wideBranchInstruction._opcode) {
110                return false;
111            }
112            return _target == wideBranchInstruction._target;
113    
114        }
115    
116        /**
117         * Return hash code.
118         *
119         * @return hash code
120         */
121        public int hashCode() {
122            int result;
123            result = (int)_opcode;
124            result = 29 * result + _target;
125            return result;
126        }
127    
128        /**
129         * Return an array of target indices.
130         *
131         * @return array of target indices.
132         */
133        public short[] getBranchTargets() {
134            return new short[]{
135                (short)_target
136            };
137        }
138    
139        /**
140         * Set the branch target indices.
141         *
142         * @param branchTargets array of target indices
143         */
144        public void setBranchTargets(short[] branchTargets) {
145            if (1 != branchTargets.length) {
146                throw new IllegalArgumentException("Wide branch instruction can only have one target");
147            }
148            _target = branchTargets[0];
149        }
150    
151    
152        /**
153         * Return instruction in human-readable form.
154         *
155         * @return string representation
156         */
157        public String toString() {
158            StringBuffer x = new StringBuffer();
159            x.append(Opcode.getOpcodeName(_opcode));
160            x.append(' ');
161            x.append(_target);
162            return x.toString();
163        }
164    }
165