001 package serp.bytecode; 002 003 import java.io.*; 004 import java.util.*; 005 006 import serp.bytecode.visitor.*; 007 import serp.util.*; 008 009 /** 010 * The <code>lookupswitch</code> instruction. 011 * 012 * @author Abe White 013 */ 014 public class LookupSwitchInstruction extends JumpInstruction { 015 // case info 016 private List _matches = new LinkedList(); 017 private List _cases = new LinkedList(); 018 019 LookupSwitchInstruction(Code owner) { 020 super(owner, Constants.LOOKUPSWITCH); 021 } 022 023 int getLength() { 024 // don't call super.getLength(), cause JumpInstruction will return 025 // value assuming this is an 'if' or 'goto' instruction 026 int length = 1; 027 028 // make the first byte of the 'default' a multiple of 4 from the 029 // start of the method 030 int byteIndex = getByteIndex() + 1; 031 for (; (byteIndex % 4) != 0; byteIndex++, length++); 032 033 // default, npairs 034 length += 8; 035 036 // pairs 037 length += (8 * _matches.size()); 038 return length; 039 } 040 041 public int getStackChange() { 042 return -1; 043 } 044 045 /** 046 * Synonymous with {@link #getTarget}. 047 */ 048 public Instruction getDefaultTarget() { 049 return getTarget(); 050 } 051 052 /** 053 * Synonymous with {@link #setTarget}. 054 */ 055 public LookupSwitchInstruction setDefaultTarget(Instruction ins) { 056 return (LookupSwitchInstruction) setTarget(ins); 057 } 058 059 /** 060 * Synonymous with {@link #getOffset}. 061 */ 062 public int getDefaultOffset() { 063 return getOffset(); 064 } 065 066 /** 067 * Synonymous with {@link #setOffset}. 068 */ 069 public LookupSwitchInstruction setDefaultOffset(int offset) { 070 setOffset(offset); 071 return this; 072 } 073 074 /** 075 * Set the match-jumppt pairs for this switch. 076 * 077 * @return this instruction, for method chaining 078 */ 079 public LookupSwitchInstruction setCases(int[] matches, 080 Instruction[] targets) { 081 _matches.clear(); 082 _cases.clear(); 083 for (int i = 0; i < matches.length; i++) 084 _matches.add(Numbers.valueOf(matches[i])); 085 for (int i = 0; i < targets.length; i++) { 086 InstructionPtrStrategy next = new InstructionPtrStrategy(this); 087 next.setTargetInstruction(targets[i]); 088 _cases.add(next); 089 } 090 invalidateByteIndexes(); 091 return this; 092 } 093 094 public int[] getOffsets() { 095 int bi = getByteIndex(); 096 int[] offsets = new int[_cases.size()]; 097 for (int i = 0; i < offsets.length; i++) 098 offsets[i] = ((InstructionPtrStrategy) _cases.get(i)).getByteIndex() 099 - bi; 100 return offsets; 101 } 102 103 /** 104 * Return the values of the case statements for this switch. 105 */ 106 public int[] getMatches() { 107 int[] matches = new int[_matches.size()]; 108 Iterator itr = _matches.iterator(); 109 for (int i = 0; i < matches.length; i++) 110 matches[i] = ((Integer) itr.next()).intValue(); 111 return matches; 112 } 113 114 /** 115 * Return the targets of the case statements for this switch. 116 */ 117 public Instruction[] getTargets() { 118 Instruction[] result = new Instruction[_cases.size()]; 119 for (int i = 0; i < result.length; i++) 120 result[i] = ((InstructionPtrStrategy) _cases.get(i)). 121 getTargetInstruction(); 122 return result; 123 } 124 125 /** 126 * Add a case to this switch. 127 * 128 * @return this instruction, for method chaining 129 */ 130 public LookupSwitchInstruction addCase(int match, Instruction target) { 131 _matches.add(Numbers.valueOf(match)); 132 _cases.add(new InstructionPtrStrategy(this, target)); 133 invalidateByteIndexes(); 134 return this; 135 } 136 137 private Instruction findJumpPoint(int jumpByteIndex, List inss) { 138 Instruction ins; 139 for (Iterator itr = inss.iterator(); itr.hasNext();) { 140 ins = (Instruction) itr.next(); 141 if (ins.getByteIndex() == jumpByteIndex) 142 return ins; 143 } 144 return null; 145 } 146 147 public void updateTargets() { 148 super.updateTargets(); 149 for (Iterator itr = _cases.iterator(); itr.hasNext();) 150 ((InstructionPtrStrategy) itr.next()).updateTargets(); 151 } 152 153 public void replaceTarget(Instruction oldTarget, Instruction newTarget) { 154 super.replaceTarget(oldTarget, newTarget); 155 for (Iterator itr = _cases.iterator(); itr.hasNext();) 156 ((InstructionPtrStrategy) itr.next()).replaceTarget(oldTarget, 157 newTarget); 158 } 159 160 public void acceptVisit(BCVisitor visit) { 161 visit.enterLookupSwitchInstruction(this); 162 visit.exitLookupSwitchInstruction(this); 163 } 164 165 void read(Instruction orig) { 166 super.read(orig); 167 168 LookupSwitchInstruction ins = (LookupSwitchInstruction) orig; 169 _matches = new LinkedList(ins._matches); 170 _cases.clear(); 171 for (Iterator itr = ins._cases.iterator(); itr.hasNext();) { 172 InstructionPtrStrategy origPtr = (InstructionPtrStrategy)itr.next(); 173 InstructionPtrStrategy newPtr = new InstructionPtrStrategy(this); 174 newPtr.setByteIndex(origPtr.getByteIndex()); 175 _cases.add(newPtr); 176 } 177 invalidateByteIndexes(); 178 } 179 180 void read(DataInput in) throws IOException { 181 // don't call super 182 int bi = getByteIndex(); 183 for (int byteIndex = bi + 1; (byteIndex % 4) != 0; byteIndex++) 184 in.readByte(); 185 186 setOffset(in.readInt()); 187 _matches.clear(); 188 _cases.clear(); 189 for (int i = 0, pairCount = in.readInt(); i < pairCount; i++) { 190 _matches.add(Numbers.valueOf(in.readInt())); 191 InstructionPtrStrategy next = new InstructionPtrStrategy(this); 192 next.setByteIndex(bi + in.readInt()); 193 _cases.add(next); 194 } 195 } 196 197 void write(DataOutput out) throws IOException { 198 // don't call super 199 int bi = getByteIndex(); 200 for (int byteIndex = bi + 1; (byteIndex % 4) != 0; byteIndex++) 201 out.writeByte(0); 202 203 out.writeInt(getOffset()); 204 out.writeInt(_matches.size()); 205 for (int i = 0; i < _matches.size(); i++) { 206 out.writeInt(((Integer) _matches.get(i)).intValue()); 207 out.writeInt(((InstructionPtrStrategy) _cases.get(i)).getByteIndex() 208 - bi); 209 } 210 } 211 }