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