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