Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

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 }