Source code: jreversepro/revengine/JSwitchTable.java
1 /**
2 * @(#)JSwitchTable.java
3 *
4 * JReversePro - Java Decompiler / Disassembler.
5 * Copyright (C) 2000 2001 Karthik Kumar.
6 * EMail: akkumar@users.sourceforge.net
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it , under the terms of the GNU General Public License as published
10 * by the Free Software Foundation; either version 2 of the License,
11 * or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 * See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program.If not, write to
19 * The Free Software Foundation, Inc.,
20 * 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
22 **/
23 package jreversepro.revengine;
24
25 import jreversepro.common.Helper;
26 import jreversepro.common.KeyWords;
27 import jreversepro.common.JJvmOpcodes;
28
29 import jreversepro.reflect.JInstruction;
30 import jreversepro.reflect.JMethod;
31
32 import jreversepro.runtime.Operand;
33
34 import java.io.DataInputStream;
35 import java.io.ByteArrayInputStream;
36 import java.io.IOException;
37
38 import java.util.Vector;
39 import java.util.Collections;
40 import java.util.Map;
41 import java.util.List;
42 import java.util.HashMap;
43
44
45 /** JSwitchTable represents the 'switch' statement as entry pairs as
46 * follows.
47 * Pair = { CaseValues, Targets }.
48 * @author Karthik Kumar
49 **/
50 public class JSwitchTable implements KeyWords,
51 JJvmOpcodes {
52 /**
53 * Instruction index of the switch statement.
54 **/
55 int insIndex;
56
57 /**
58 * List of cases that are available.
59 * Individual elements are JCaseEntry.
60 **/
61 List cases;
62
63 /**
64 * DefaultByte for this switch statement.
65 **/
66 int defaultByte;
67
68 /** Max Target from this switch statement group **/
69 int maxTarget;
70
71 /**
72 * Name of the variable that is put under switch statement.
73 **/
74 String varName;
75
76 /**
77 * Datatype of the variable for which switch is used.
78 **/
79 String datatype;
80
81 /**
82 * Reference to method to which this
83 * switch table belongs.
84 **/
85 JMethod method;
86
87 /**
88 * @param method Reference to current method.
89 * @param ins Instruction that corresponds to a
90 * tableswitch or a lookupswitch instruction.
91 * @param gotos Map of goto statements.
92 *
93 * @throws IOException thrown in case of any error.
94 * @throws RevEngineException if the instruction passed is not
95 * a switch opcode.
96 **/
97 public JSwitchTable(JMethod method, JInstruction ins, Map gotos)
98 throws RevEngineException,
99 IOException {
100
101 this.method = method;
102 insIndex = ins.index;
103 cases = new Vector();
104
105 if (ins.opcode == OPCODE_TABLESWITCH) {
106 createTableSwitch(ins.args, ins.index, gotos);
107 } else if (ins.opcode == OPCODE_LOOKUPSWITCH) {
108 createLookupSwitch(ins.args, ins.index, gotos);
109 } else {
110 throw new RevEngineException("Not a switch statement");
111 }
112 datatype = null;
113 }
114
115 /**
116 * @param method Reference to current method.
117 * @param ins Instruction that corresponds to a
118 * tableswitch or a lookupswitch instruction.
119 * @param op1 Operand that is to be used inside the switch statement.
120 * @param gotos Map of goto statements.
121 * @throws IOException thrown in case of any error.
122 * @throws RevEngineException if the instruction passed is not
123 * a switch opcode.
124 **/
125 public JSwitchTable(JMethod method, JInstruction ins, Operand op1,
126 Map gotos)
127 throws RevEngineException,
128 IOException {
129 this.datatype = op1.getDatatype();
130 this.varName = op1.getValue();
131
132 //Copy - paste from prev. constructor.
133 this.method = method;
134 insIndex = ins.index;
135 cases = new Vector();
136
137 if (ins.opcode == OPCODE_TABLESWITCH) {
138 createTableSwitch(ins.args, ins.index, gotos);
139 } else if (ins.opcode == OPCODE_LOOKUPSWITCH) {
140 createLookupSwitch(ins.args, ins.index, gotos);
141 } else {
142 throw new RevEngineException("Not a switch statement");
143 }
144 Helper.log("switch datatype " + datatype);
145 }
146
147
148 /**
149 * @return Returns the default byte of this switch block.
150 **/
151 public int getDefaultByte() {
152 return defaultByte;
153 }
154
155 /**
156 * For 'tableswitch' opcode this fills the data structure -
157 * JSwitchTable.
158 * @param entries Bytecode entries that contain the case values
159 * and their target opcodes.
160 * @param offset offset is the index of the current tableswitch
161 * instruction into the method bytecode array.
162 * @param gotos Map of goto statements.
163 * @throws IOException Thrown in case of an i/o error when reading
164 * from the bytes.
165 **/
166 private void createTableSwitch(byte [] entries, int offset, Map gotos)
167 throws IOException {
168 DataInputStream dis = new DataInputStream(
169 new ByteArrayInputStream(entries));
170 defaultByte = dis.readInt() + offset;
171 int lowVal = dis.readInt();
172 int highVal = dis.readInt();
173
174 Map mapCases = new HashMap();
175 for (int i = lowVal; i <= highVal; i++) {
176 int curTarget = dis.readInt() + offset;
177 String value = Helper.getValue(String.valueOf(i),
178 this.datatype);
179 Object obj = mapCases.get(new Integer(curTarget));
180 if (obj == null) {
181 mapCases.put(new Integer(curTarget),
182 new JCaseEntry(value, curTarget));
183 } else {
184 JCaseEntry ent = (JCaseEntry) obj;
185 ent.addValue(value);
186 }
187 }
188 cases = new Vector(mapCases.values());
189 dis.close();
190 processData(gotos);
191 }
192
193 /**
194 * For 'lookupswitch' opcode this fills the data structure -
195 * JSwitchTable.
196 * @param entries Bytecode entries that contain the case values
197 * and their target opcodes.
198 * @param offset offset is the index of the current lookupswitch
199 * instruction into the method bytecode array.
200 * @param gotos Map of goto statements.
201 *
202 * @throws IOException Thrown in case of an i/o error when reading
203 * from the bytes.
204 **/
205 private void createLookupSwitch(byte [] entries, int offset, Map gotos)
206 throws IOException {
207 DataInputStream dis = new DataInputStream(
208 new ByteArrayInputStream(entries));
209
210 defaultByte = dis.readInt() + offset;
211 int numVal = dis.readInt();
212
213 Map mapCases = new HashMap();
214
215 for (int i = 0; i < numVal; i++) {
216 String value =
217 Helper.getValue(String.valueOf(dis.readInt()),
218 datatype);
219 int curTarget = dis.readInt() + offset;
220
221 Object obj = mapCases.get(new Integer(curTarget));
222 if (obj == null) {
223 mapCases.put(
224 new Integer(curTarget),
225 new JCaseEntry(value, curTarget));
226 } else {
227 JCaseEntry ent = (JCaseEntry) obj;
228 ent.addValue(value);
229 }
230 }
231 cases = new Vector(mapCases.values());
232 dis.close();
233 processData(gotos);
234 }
235
236 /**
237 * @return Returns the list of cases.
238 * Individual elements are JCaseEntry.
239 **/
240 public List getCases() {
241 return cases;
242 }
243
244 /**
245 * @return Returns the disassembled string for this switch
246 * statement block.
247 **/
248 public String disassemble() {
249 StringBuffer sb = new StringBuffer("");
250 for (int i = 0; i < cases.size() ; i++) {
251 sb.append("\n\t\t\t" + cases.get(i));
252 }
253 sb.append("\n\t\t\tDefault Byte " + defaultByte);
254 return sb.toString();
255 }
256
257 /**
258 * @param rhsType Type of the switch variable of this block.
259 * @param rhsValue Value ( name ) of the switch variable for this
260 * block.
261 **/
262 public void setTypeValue(String rhsType, String rhsValue) {
263 varName = rhsValue;
264 datatype = rhsType;
265 //dataType could be either int or char.
266 }
267
268 /**
269 * Process the bytecode stream of case values and targets to
270 * individual case blocks.
271 * @param gotos Map of goto statements.
272 **/
273 public void processData(Map gotos) {
274 maxTarget = defaultByte;
275 if (gotos != null) {
276 for (int i = 0; i < cases.size() - 1; i++) {
277 JCaseEntry ent = (JCaseEntry) cases.get(i);
278 Object obj = gotos.get(new Integer(ent.getTarget() - 3));
279 if (obj != null) {
280 int tempVal = ((Integer) obj).intValue();
281 maxTarget = (maxTarget > tempVal) ? maxTarget : tempVal;
282 }
283 }
284 if (maxTarget > defaultByte) {
285 boolean targetPresent = false;
286 for (int i = 0; i < cases.size() - 1; i++) {
287 JCaseEntry ent = (JCaseEntry) cases.get(i);
288 if (ent.getTarget() == defaultByte) {
289 ent.addValue(DEFAULT);
290 //ent.setTarget(defa);
291 targetPresent = true;
292 }
293 }
294 if (!targetPresent) {
295 cases.add(new JCaseEntry(DEFAULT, defaultByte));
296 }
297 }
298 }
299
300 //Sort the entries
301 Collections.sort(cases, new JCaseComparator());
302
303 //Assign endTargets for all of them.
304 int i = 0;
305 for (; i < cases.size() - 1; i++) {
306 JCaseEntry ent = (JCaseEntry) cases.get(i);
307 JCaseEntry entNext = (JCaseEntry) cases.get(i + 1);
308 ent.setEndTarget(entNext.getTarget());
309 }
310 JCaseEntry entLast = (JCaseEntry) cases.get(i);
311 entLast.setEndTarget(maxTarget);
312 }
313
314 /**
315 * @return Returns a branch entry for this switch statement.
316 **/
317 public JBranchEntry getBranchEntry() {
318 return new JBranchEntry(method,
319 insIndex, maxTarget, maxTarget,
320 BranchConstants.TYPE_SWITCH,
321 varName, "", "");
322 }
323
324 /**
325 * Inserts a CaseEntry in the list.
326 * @param caseEntry Case Entry to be appended.
327 **/
328 public void addCaseEntry(JCaseEntry caseEntry) {
329 cases.add(caseEntry);
330 }
331
332 /**
333 * @return Returns the Stringified version of this class.
334 **/
335 public String toString() {
336 return cases.toString();
337 }
338 }