001 package serp.bytecode; 002 003 import java.io.*; 004 import java.util.*; 005 006 /** 007 * Contains functionality common to the different switch types 008 * (TableSwitch and LookupSwitch). 009 * 010 * @author Eric Lindauer 011 */ 012 public abstract class SwitchInstruction extends JumpInstruction { 013 private List _cases = new LinkedList(); 014 015 public SwitchInstruction(Code owner, int opcode) { 016 super(owner, opcode); 017 } 018 019 /** 020 * Returns the current byte offsets for the different 021 * switch cases in this Instruction. 022 */ 023 public int[] getOffsets() { 024 int bi = getByteIndex(); 025 int[] offsets = new int[_cases.size()]; 026 for (int i = 0; i < offsets.length; i++) 027 offsets[i] = ((InstructionPtrStrategy) _cases.get(i)).getByteIndex() 028 - bi; 029 return offsets; 030 } 031 032 /** 033 * Sets the offsets for the instructions representing the different 034 * switch statement cases. WARNING: these offsets will not be changed 035 * in the event that the code is modified following this call. It is 036 * typically a good idea to follow this call with a call to updateTargets 037 * as soon as the instructions at the given offsets are valid, at which 038 * point the Instructions themselves will be used as the targets and the 039 * offsets will be updated as expected. 040 */ 041 public void setOffsets(int[] offsets) { 042 int bi = getByteIndex(); 043 _cases.clear(); 044 for (int i = 0; i < offsets.length; i++) { 045 InstructionPtrStrategy next = new InstructionPtrStrategy(this); 046 next.setByteIndex(offsets[i] + bi); 047 _cases.add(next); 048 } 049 } 050 051 public int countTargets() { 052 return _cases.size(); 053 } 054 055 int getLength() { 056 // don't call super.getLength(), cause JumpInstruction will return 057 // value assuming this is an 'if' or 'goto' instruction 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 return length; 065 } 066 067 /** 068 * Synonymous with {@link #getTarget}. 069 */ 070 public Instruction getDefaultTarget() { 071 return getTarget(); 072 } 073 074 /** 075 * Synonymous with {@link #getOffset}. 076 */ 077 public int getDefaultOffset() { 078 return getOffset(); 079 } 080 081 /** 082 * Synonymous with {@link #setOffset}. 083 */ 084 public SwitchInstruction setDefaultOffset(int offset) { 085 setOffset(offset); 086 return this; 087 } 088 089 /** 090 * Synonymous with {@link #setTarget}. 091 */ 092 public SwitchInstruction setDefaultTarget(Instruction ins) { 093 return (SwitchInstruction) setTarget(ins); 094 } 095 096 /** 097 * Return the targets for this switch, or empty array if not set. 098 */ 099 public Instruction[] getTargets() { 100 Instruction[] result = new Instruction[_cases.size()]; 101 for (int i = 0; i < _cases.size(); i++) 102 result[i] = ((InstructionPtrStrategy) _cases.get(i)). 103 getTargetInstruction(); 104 return result; 105 } 106 107 /** 108 * Set the jump points for this switch. 109 * 110 * @return this instruction, for method chaining 111 */ 112 public SwitchInstruction setTargets(Instruction[] targets) { 113 _cases.clear(); 114 if (targets != null) 115 for (int i = 0; i < targets.length; i++) 116 addTarget(targets[i]); 117 return this; 118 } 119 120 /** 121 * Add a target to this switch. 122 * 123 * @return this instruction, for method chaining 124 */ 125 public SwitchInstruction addTarget(Instruction target) { 126 _cases.add(new InstructionPtrStrategy(this, target)); 127 return this; 128 } 129 130 public int getStackChange() { 131 return -1; 132 } 133 134 public void updateTargets() { 135 super.updateTargets(); 136 for (Iterator itr = _cases.iterator(); itr.hasNext();) 137 ((InstructionPtrStrategy) itr.next()).updateTargets(); 138 } 139 140 public void replaceTarget(Instruction oldTarget, Instruction newTarget) { 141 super.replaceTarget(oldTarget, newTarget); 142 for (Iterator itr = _cases.iterator(); itr.hasNext();) 143 ((InstructionPtrStrategy) itr.next()).replaceTarget(oldTarget, 144 newTarget); 145 } 146 147 void read(Instruction orig) { 148 super.read(orig); 149 150 SwitchInstruction ins = (SwitchInstruction) orig; 151 _cases.clear(); 152 InstructionPtrStrategy incoming; 153 for (Iterator itr = ins._cases.iterator(); itr.hasNext();) { 154 incoming = (InstructionPtrStrategy) itr.next(); 155 InstructionPtrStrategy next = new InstructionPtrStrategy(this); 156 next.setByteIndex(incoming.getByteIndex()); 157 _cases.add(next); 158 } 159 } 160 161 void clearTargets() { 162 _cases.clear(); 163 } 164 165 void readTarget(DataInput in) throws IOException { 166 InstructionPtrStrategy next = new InstructionPtrStrategy(this); 167 next.setByteIndex(getByteIndex() + in.readInt()); 168 _cases.add(next); 169 } 170 171 /** 172 * Set the match-jumppt pairs for this switch. 173 * 174 * @return this instruction, for method chaining 175 */ 176 public SwitchInstruction setCases(int[] matches, Instruction[] targets) { 177 setMatches(matches); 178 setTargets(targets); 179 return this; 180 } 181 182 public SwitchInstruction setMatches(int[] matches) { 183 clearMatches(); 184 for (int i = 0; i < matches.length; i++) 185 addMatch(matches[i]); 186 return this; 187 } 188 189 /** 190 * Add a case to this switch. 191 * 192 * @return this instruction, for method chaining 193 */ 194 public SwitchInstruction addCase(int match, Instruction target) { 195 addMatch(match); 196 addTarget(target); 197 return this; 198 } 199 200 public abstract SwitchInstruction addMatch(int match); 201 202 public abstract int[] getMatches(); 203 204 abstract void clearMatches(); 205 206 void calculateOpcode() { 207 } 208 }