001 package sysModel.classFile.code.instructions; 002 003 import sysModel.classFile.code.Opcode; 004 import sysModel.classFile.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 short getBytecodeLength(short pc) { 069 int pad = 3 - (pc % 4); 070 return (short)(pad + 9 + (_targets.length) * 4); 071 } 072 073 /** 074 * Make a new TABLESWITCH instruction from the bytecode stating at pc, padded using paddingPC, and use the line 075 * number table for branches. 076 * 077 * @param bytecode bytecode 078 * @param pc starting index in bytecode 079 * @param paddingPC PC for padding 080 * @param lnt line number table for branches 081 */ 082 public TableSwitchInstruction(byte[] bytecode, short pc, short paddingPC, LineNumberTable lnt) { 083 int[] branchTargets = Opcode.getBranchTargets(bytecode, pc, paddingPC); 084 int pad = 3 - (paddingPC % 4); 085 _low = Types.intFromBytes(bytecode, pc + pad + 5); 086 _high = Types.intFromBytes(bytecode, pc + pad + 9); 087 088 _targets = new int[branchTargets.length]; 089 int i = 0; 090 for(int bt : branchTargets) { 091 _targets[i++] = lnt.getLineNumber((short)bt); 092 } 093 } 094 095 /** 096 * Get the bytecode for this instruction, padded for the specified program counter value, with branch targets 097 * according to the specified line number table 098 * 099 * @param pc PC for padding 100 * @param lnt line number table for branches 101 * 102 * @return bytecode 103 */ 104 public byte[] getBytecode(short pc, LineNumberTable lnt) { 105 int pad = 3 - (pc % 4); 106 byte[] b = new byte[getBytecodeLength(pc)]; 107 b[0] = getOpcode(); 108 Types.bytesFromInt(lnt.getPC((short)_targets[0]) - pc, b, pad + 1); 109 Types.bytesFromInt((int)_low, b, pad + 5); 110 Types.bytesFromInt((int)_high, b, pad + 9); 111 for(int i = 1; i < _targets.length; ++i) { 112 Types.bytesFromInt(lnt.getPC((short)_targets[i]) - pc, b, pad + 9 + i * 4); 113 } 114 return b; 115 } 116 117 /** 118 * Return true of this instruction and the other object are equal. 119 * 120 * @param o other object 121 * 122 * @return true if equal 123 */ 124 public boolean equals(Object o) { 125 if (this == o) { 126 return true; 127 } 128 if (!(o instanceof TableSwitchInstruction)) { 129 return false; 130 } 131 132 TableSwitchInstruction tableSwitchInstruction = (TableSwitchInstruction)o; 133 134 if (_high != tableSwitchInstruction._high) { 135 return false; 136 } 137 if (_low != tableSwitchInstruction._low) { 138 return false; 139 } 140 return Arrays.equals(_targets, tableSwitchInstruction._targets); 141 142 } 143 144 /** 145 * Return hash code. 146 * 147 * @return hash code 148 */ 149 public int hashCode() { 150 int result; 151 result = (int)(_low ^ (_low >>> 32)); 152 result = 29 * result + (int)(_high ^ (_high >>> 32)); 153 return result; 154 } 155 156 /** 157 * Return an array of target indices. 158 * 159 * @return array of target indices. 160 */ 161 public short[] getBranchTargets() { 162 short[] bt = new short[_targets.length]; 163 int i = 0; 164 for(int t : _targets) { 165 bt[i++] = (short)t; 166 } 167 return bt; 168 } 169 170 /** 171 * Set the branch target indices. 172 * 173 * @param branchTargets array of target indices 174 */ 175 public void setBranchTargets(short[] branchTargets) { 176 if (branchTargets.length != _targets.length) { 177 throw new IllegalArgumentException("TABLESWITCH has incorrect number of targets"); 178 } 179 for(int i = 0; i < branchTargets.length; ++i) { 180 _targets[i] = branchTargets[i]; 181 } 182 } 183 184 /** 185 * Return instruction in human-readable form. 186 * 187 * @return string representation 188 */ 189 public String toString() { 190 StringBuffer x = new StringBuffer(); 191 x.append(Opcode.getOpcodeName(Opcode.TABLESWITCH)); 192 x.append(" default = "); 193 x.append(_targets[0]); 194 x.append(" low = "); 195 x.append(_low); 196 x.append(" high = "); 197 x.append(_high); 198 x.append(" n = "); 199 x.append(_targets.length - 1); 200 for(int i = 0; i < _targets.length - 1; ++i) { 201 x.append(" ("); 202 x.append(_low + i); 203 x.append("->"); 204 x.append(_targets[i + 1]); 205 x.append(')'); 206 } 207 return x.toString(); 208 } 209 } 210