001 package serp.bytecode; 002 003 import java.io.*; 004 import java.util.*; 005 006 import serp.bytecode.visitor.*; 007 008 /** 009 * The <code>tableswitch</code> instruction. 010 * 011 * @author Abe White 012 */ 013 public class TableSwitchInstruction extends JumpInstruction { 014 // case info 015 private int _low = 0; 016 private int _high = 0; 017 private List _cases = new LinkedList(); 018 019 TableSwitchInstruction(Code owner) { 020 super(owner, Constants.TABLESWITCH); 021 } 022 023 /** 024 * Returns the current byte offsets for the different 025 * switch cases in this Instruction. 026 */ 027 public int[] getOffsets() { 028 int bi = getByteIndex(); 029 int[] offsets = new int[_cases.size()]; 030 for (int i = 0; i < _cases.size(); i++) 031 offsets[i] = ((InstructionPtrStrategy) _cases.get(i)).getByteIndex() 032 - bi; 033 return offsets; 034 } 035 036 /** 037 * Sets the offsets for the instructions representing the different 038 * switch statement cases. WARNING: these offsets will not be changed 039 * in the event that the code is modified following this call. It is 040 * typically a good idea to follow this call with a call to updateTargets 041 * as soon as the instructions at the given offsets are valid, at which 042 * point the Instructions themselves will be used as the targets and the 043 * offsets will be updated as expected. 044 */ 045 public void setOffsets(int[] offsets) { 046 int bi = getByteIndex(); 047 _cases.clear(); 048 for (int i = 0; i < offsets.length; i++) { 049 InstructionPtrStrategy next = new InstructionPtrStrategy(this); 050 next.setByteIndex(offsets[i] + bi); 051 _cases.add(next); 052 } 053 invalidateByteIndexes(); 054 } 055 056 int getLength() { 057 // don't call super 058 int length = 1; 059 060 // make the first byte of the 'default' a multiple of 4 from the 061 // start of the method 062 int byteIndex = getByteIndex() + 1; 063 for (; (byteIndex % 4) != 0; byteIndex++, length++); 064 065 // default, low, high 066 length += 12; 067 068 // offsets 069 length += (4 * _cases.size()); 070 return length; 071 } 072 073 /** 074 * Synonymous with {@link #getTarget}. 075 */ 076 public Instruction getDefaultTarget() { 077 return getTarget(); 078 } 079 080 /** 081 * Synonymous with {@link #setTarget}. 082 */ 083 public TableSwitchInstruction setDefaultTarget(Instruction ins) { 084 return (TableSwitchInstruction) setTarget(ins); 085 } 086 087 /** 088 * Synonymous with {@link #getOffset}. 089 */ 090 public int getDefaultOffset() { 091 return getOffset(); 092 } 093 094 /** 095 * Synonymous with {@link #setOffset}. 096 */ 097 public TableSwitchInstruction setDefaultOffset(int offset) { 098 setOffset(offset); 099 return this; 100 } 101 102 public int getLow() { 103 return _low; 104 } 105 106 public TableSwitchInstruction setLow(int low) { 107 _low = low; 108 return this; 109 } 110 111 public int getHigh() { 112 return _high; 113 } 114 115 public TableSwitchInstruction setHigh(int high) { 116 _high = high; 117 return this; 118 } 119 120 /** 121 * Return the targets for this switch, or empty array if not set. 122 */ 123 public Instruction[] getTargets() { 124 Instruction[] result = new Instruction[_cases.size()]; 125 for (int i = 0; i < _cases.size(); i++) 126 result[i] = ((InstructionPtrStrategy) _cases.get(i)). 127 getTargetInstruction(); 128 return result; 129 } 130 131 /** 132 * Set the jump points for this switch. 133 * 134 * @return this instruction, for method chaining 135 */ 136 public TableSwitchInstruction setTargets(Instruction[] targets) { 137 _cases.clear(); 138 if (targets != null) 139 for (int i = 0; i < targets.length; i++) 140 addTarget(targets[i]); 141 return this; 142 } 143 144 /** 145 * Add a target to this switch. 146 * 147 * @return this instruction, for method chaining 148 */ 149 public TableSwitchInstruction addTarget(Instruction target) { 150 _cases.add(new InstructionPtrStrategy(this, target)); 151 invalidateByteIndexes(); 152 return this; 153 } 154 155 public int getStackChange() { 156 return -1; 157 } 158 159 private Instruction findTarget(int jumpByteIndex, List inss) { 160 Instruction ins; 161 for (Iterator itr = inss.iterator(); itr.hasNext();) { 162 ins = (Instruction) itr.next(); 163 if (ins.getByteIndex() == jumpByteIndex) 164 return ins; 165 } 166 return null; 167 } 168 169 public void updateTargets() { 170 super.updateTargets(); 171 for (Iterator itr = _cases.iterator(); itr.hasNext();) 172 ((InstructionPtrStrategy) itr.next()).updateTargets(); 173 } 174 175 public void replaceTarget(Instruction oldTarget, Instruction newTarget) { 176 super.replaceTarget(oldTarget, newTarget); 177 for (Iterator itr = _cases.iterator(); itr.hasNext();) 178 ((InstructionPtrStrategy) itr.next()).replaceTarget(oldTarget, 179 newTarget); 180 } 181 182 public void acceptVisit(BCVisitor visit) { 183 visit.enterTableSwitchInstruction(this); 184 visit.exitTableSwitchInstruction(this); 185 } 186 187 void read(Instruction orig) { 188 super.read(orig); 189 190 TableSwitchInstruction ins = (TableSwitchInstruction) orig; 191 setLow(ins.getLow()); 192 setHigh(ins.getHigh()); 193 InstructionPtrStrategy incoming; 194 for (Iterator itr = ins._cases.iterator(); itr.hasNext();) { 195 incoming = (InstructionPtrStrategy) itr.next(); 196 InstructionPtrStrategy next = new InstructionPtrStrategy(this); 197 next.setByteIndex(incoming.getByteIndex()); 198 _cases.add(next); 199 } 200 invalidateByteIndexes(); 201 } 202 203 void read(DataInput in) throws IOException { 204 // don't call super 205 int bi = getByteIndex(); 206 for (int byteIndex = bi + 1; (byteIndex % 4) != 0; byteIndex++) 207 in.readByte(); 208 209 setOffset(in.readInt()); 210 setLow(in.readInt()); 211 setHigh(in.readInt()); 212 _cases.clear(); 213 for (int i = 0; i < (_high - _low + 1); i++) { 214 InstructionPtrStrategy next = new InstructionPtrStrategy(this); 215 next.setByteIndex(bi + in.readInt()); 216 _cases.add(next); 217 } 218 } 219 220 void write(DataOutput out) throws IOException { 221 // don't call super 222 int bi = getByteIndex(); 223 for (int byteIndex = bi + 1; (byteIndex % 4) != 0; byteIndex++) 224 out.writeByte(0); 225 226 out.writeInt(getOffset()); 227 out.writeInt(getLow()); 228 out.writeInt(getHigh()); 229 for (Iterator itr = _cases.iterator(); itr.hasNext();) 230 out.writeInt(((InstructionPtrStrategy) itr.next()).getByteIndex() 231 - bi); 232 } 233 }