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 import java.util.Arrays; 007 008 /** 009 * TABLESWITCH Java instruction. 010 * 011 * @author Mathias Ricken 012 */ 013 public class TableSwitchInstruction extends AInstruction { 014 /** 015 * Lower bound. 016 */ 017 protected long _low; 018 019 /** 020 * Upper bound. 021 */ 022 protected long _high; 023 024 /** 025 * Branch targets. 026 */ 027 protected int[] _targets; 028 029 /** 030 * Constructor. 031 * 032 * @param defaultTarget default target line number 033 * @param low lower bound 034 * @param high upper bound 035 * @param targets target line nu,bers 036 */ 037 public TableSwitchInstruction(int defaultTarget, long low, long high, int[] targets) { 038 if (low > high) { 039 throw new IllegalArgumentException("High cannot be greater than low."); 040 } 041 _low = low; 042 _high = high; 043 long npairs = high - low + 1; 044 if (npairs != targets.length) { 045 throw new IllegalArgumentException("Invalid number of targets"); 046 } 047 _targets = new int[(int)npairs + 1]; 048 _targets[0] = defaultTarget; 049 System.arraycopy(targets, 0, _targets, 1, (int)npairs); 050 } 051 052 /** 053 * Get the opcode of this instruction. 054 * 055 * @return opcode 056 */ 057 public byte getOpcode() { 058 return Opcode.TABLESWITCH; 059 } 060 061 /** 062 * Get the length bytecode for this instruction, padded for the specified program counter value. 063 * 064 * @param pc PC for padding 065 * 066 * @return bytecode length 067 */ 068 public int getBytecodeLength(int pc) { 069 int pad = 3 - (pc % 4); 070 int res = pad + 9 + (_targets.length) * 4; 071 assert(res <= 0xffff); 072 return res; 073 } 074 075 /** 076 * Make a new TABLESWITCH instruction from the bytecode stating at pc, padded using paddingPC, and use the line 077 * number table for branches. 078 * 079 * @param bytecode bytecode 080 * @param pc starting index in bytecode 081 * @param paddingPC PC for padding 082 * @param lnt line number table for branches 083 */ 084 public TableSwitchInstruction(byte[] bytecode, int pc, int paddingPC, LineNumberTable lnt) { 085 int[] branchTargets = Opcode.getBranchTargets(bytecode, pc, paddingPC); 086 int pad = 3 - (paddingPC % 4); 087 _low = Types.intFromBytes(bytecode, pc + pad + 5); 088 _high = Types.intFromBytes(bytecode, pc + pad + 9); 089 090 _targets = new int[branchTargets.length]; 091 int i = 0; 092 for(int bt : branchTargets) { 093 _targets[i++] = lnt.getLineNumber(bt); 094 } 095 } 096 097 /** 098 * Get the bytecode for this instruction, padded for the specified program counter value, with branch targets 099 * according to the specified line number table 100 * 101 * @param pc PC for padding 102 * @param lnt line number table for branches 103 * 104 * @return bytecode 105 */ 106 public byte[] getBytecode(int pc, LineNumberTable lnt) { 107 int pad = 3 - (pc % 4); 108 byte[] b = new byte[getBytecodeLength(pc)]; 109 b[0] = getOpcode(); 110 Types.bytesFromInt(lnt.getPC(_targets[0]) - pc, b, pad + 1); 111 Types.bytesFromInt((int)_low, b, pad + 5); 112 Types.bytesFromInt((int)_high, b, pad + 9); 113 for(int i = 1; i < _targets.length; ++i) { 114 Types.bytesFromInt(lnt.getPC(_targets[i]) - pc, b, pad + 9 + i * 4); 115 } 116 return b; 117 } 118 119 /** 120 * Return true of this instruction and the other object are equal. 121 * 122 * @param o other object 123 * 124 * @return true if equal 125 */ 126 public boolean equals(Object o) { 127 if (this == o) { 128 return true; 129 } 130 if (!(o instanceof TableSwitchInstruction)) { 131 return false; 132 } 133 134 final TableSwitchInstruction tableSwitchInstruction = (TableSwitchInstruction)o; 135 136 if (_high != tableSwitchInstruction._high) { 137 return false; 138 } 139 if (_low != tableSwitchInstruction._low) { 140 return false; 141 } 142 if (!Arrays.equals(_targets, tableSwitchInstruction._targets)) { 143 return false; 144 } 145 146 return true; 147 } 148 149 /** 150 * Return hash code. 151 * 152 * @return hash code 153 */ 154 public int hashCode() { 155 int result; 156 result = (int)(_low ^ (_low >>> 32)); 157 result = 29 * result + (int)(_high ^ (_high >>> 32)); 158 return result; 159 } 160 161 /** 162 * Return an array of target indices. 163 * 164 * @return array of target indices. 165 */ 166 public int[] getBranchTargets() { 167 int[] bt = new int[_targets.length]; 168 int i = 0; 169 for(int t : _targets) { 170 bt[i++] = t; 171 } 172 return bt; 173 } 174 175 /** 176 * Set the branch target indices. 177 * 178 * @param branchTargets array of target indices 179 */ 180 public void setBranchTargets(int[] branchTargets) { 181 if (branchTargets.length != _targets.length) { 182 throw new IllegalArgumentException("TABLESWITCH has incorrect number of targets"); 183 } 184 for(int i = 0; i < branchTargets.length; ++i) { 185 assert(branchTargets[i] <= 0xffff); 186 _targets[i] = branchTargets[i]; 187 } 188 } 189 190 /** 191 * Return instruction in human-readable form. 192 * 193 * @return string representation 194 */ 195 public String toString() { 196 StringBuilder x = new StringBuilder(); 197 x.append(Opcode.getOpcodeName(Opcode.TABLESWITCH)); 198 x.append(" default = "); 199 x.append(_targets[0]); 200 x.append(" low = "); 201 x.append(_low); 202 x.append(" high = "); 203 x.append(_high); 204 x.append(" n = "); 205 x.append(_targets.length - 1); 206 for(int i = 0; i < _targets.length - 1; ++i) { 207 x.append(" ("); 208 x.append(_low + i); 209 x.append("->"); 210 x.append(_targets[i + 1]); 211 x.append(")"); 212 } 213 return x.toString(); 214 } 215 } 216