Source code: jreversepro/runtime/JSymbolTable.java
1 /*
2 * @(#)JSymbolTableImpl.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.runtime;
24
25 import jreversepro.common.KeyWords;
26 import jreversepro.common.Helper;
27
28 import jreversepro.reflect.JMethod;
29 import jreversepro.reflect.JImport;
30 import jreversepro.reflect.JInstruction;
31
32 import jreversepro.revengine.RevEngineException;
33
34 import java.util.HashMap;
35 import java.util.Map;
36 import java.util.List;
37 import java.util.Vector;
38 import java.util.Collections;
39 import java.util.Enumeration;
40 import java.util.Iterator;
41
42 /**
43 * JSymbolTable - Symbol Table of a method containing local variables only.
44 * @author Karthik Kumar
45 **/
46 public class JSymbolTable implements KeyWords {
47
48 /**
49 * Map of the Symbols -
50 * Key - local variable index - java.lang.Integer
51 * Value - List of JLocalEntry.
52 * since for the same localvariable index more than one
53 * datatype may exist.
54 **/
55 Map symbols;
56
57
58 /**
59 * Key - Symbol Name
60 * Value - JLocalEntry.
61 **/
62 Map symNames;
63
64 /**
65 * List of keywords in the language
66 **/
67 static List keywords;
68
69 /**
70 * Maximum number of symbols that can be in the table at any given
71 * time
72 **/
73 int maxSymbols;
74
75 /**
76 * Maximum args in that symbol count mentioned in maxSymbols
77 **/
78 int maxArgs;
79
80 /**
81 * basicIndex here.
82 **/
83 int basicIndex;
84
85 /**
86 * Imported Classes here.
87 **/
88 JImport imports;
89
90 /**
91 * Index of the argument into the symbol table.
92 **/
93 public static final int ARG_INDEX = -1;
94
95 /**
96 * Static initializers - Keywords
97 **/
98 static {
99 keywords = getKeyWordsList();
100 }
101
102 /**
103 * @param rhsMethod Method for which this symbol table
104 * is generated and used.
105 * @param imports ImportedClasses by this class.
106 **/
107 public JSymbolTable(JMethod rhsMethod,
108 JImport imports) {
109
110 this.imports = imports;
111 List args = rhsMethod.getArgList();
112 int startIndex = 1;
113
114 maxSymbols = rhsMethod.getMaxLocals();
115 maxArgs = args.size();
116
117 //Symbols = new HashSet();
118 symbols = new HashMap();
119 symNames = new HashMap();
120
121 basicIndex = (int) ('i');
122 //Starts with variable 'i'.
123
124
125 if (!rhsMethod.isStatic()) {
126 startIndex = 1;
127 addEntry(0, -1, THISCLASS, true);
128 //Load the first entry for this pointer.
129 //Current class reference.
130 } else {
131 maxArgs--;
132 startIndex = 0;
133 }
134
135 //Loads the Arguments onto symbol table.
136 loadSymbols(args, startIndex);
137 }
138
139 /**
140 * @return Get the maximum number of symbols
141 * available in the scope of the current method.
142 **/
143 public int getMaxSymbols() {
144 return maxSymbols;
145 }
146
147 /**
148 * @param aArgs List of arguments containing the argument
149 * types.
150 * @param startIndex Initial Start Index of the variable count.
151 **/
152 private void loadSymbols(List aArgs, int startIndex) {
153 int symIndex = startIndex;
154 for (int i = 0 ; i < aArgs.size(); i++) {
155 String argType = (String) aArgs.get(i);
156 addEntry(symIndex++, ARG_INDEX, argType, true);
157 if (argType.equals(LONG)
158 || argType.equals(DOUBLE)) {
159 //Since both long and double take two entries.
160 //increment the symbol table count
161 symIndex++;
162 }
163 }
164 }
165
166 /**
167 * Adds a new datatype to the symboltable
168 * dynamically .
169 * Parameters required are the
170 * @param aVarIndex variable index ,
171 * @param aDatatype Datatype
172 * @param aVarStore variable store index.
173 * @param aDeclared If variable is declared.
174 **/
175 public void assignDataType(int aVarIndex, String aDatatype,
176 int aVarStore, boolean aDeclared) {
177
178 if ((aVarIndex > JInstruction.INVALID_VAR_INDEX
179 && aVarIndex <= maxArgs)) {
180 return;
181 //No reassignment of data types for arguments.
182 }
183 JLocalEntry ent = getMatchingEntry(aVarIndex, aVarStore);
184 if (ent != null) {
185 if (aDatatype.equals(REFERENCE)) {
186 return;
187 }
188 } else {
189 if (aDatatype.equals(REFERENCE)) {
190 aDatatype = LANG_OBJECT;
191 }
192 }
193 addEntry(aVarIndex, aVarStore, aDatatype, aDeclared);
194 }
195
196 /**
197 * This method primarily keeps track of the last line
198 * that references the variable represented by
199 * aVarIndex and the datatype aDataType.
200 * Please note that this referencing is essential to define the
201 * variables correctly, neither too early, nor too late.
202 * Also this is not necessary for arguments, but more
203 * necessary for localvariables whose declarations are
204 * inside the method code block.
205 * @param aVarIndex variable index ,
206 * @param aDatatype Datatype
207 * @param aIndex Refernced index.
208 **/
209 public void addReference(int aVarIndex, String aDatatype,
210 int aIndex) {
211
212 if ((aVarIndex > JInstruction.INVALID_VAR_INDEX
213 && aVarIndex <= maxArgs)) {
214 return;
215 //Not necessary to keep track of referenced line numbers.
216 //Since the variable is an argument.
217 }
218 List currentList = (List) symbols.get(new Integer(aVarIndex));
219 JLocalEntry createEntry = new JLocalEntry(
220 -1, //VarIndex irrelevant
221 -1, //StoreIndex irrelevant.
222 aDatatype,
223 "", //Name irrelevant
224 false); //Declared irrelevant
225 int objIndex = currentList.indexOf(createEntry);
226 if (objIndex != -1) {
227 JLocalEntry ent = (JLocalEntry) currentList.get(objIndex);
228 ent.setLastReferredIndex(aIndex);
229 } else {
230 //Helper.log("Something seriously wrong");
231 }
232 }
233
234 /**
235 * Given the endOfBranch of a branch, this method
236 * returns a List of strings, of the form
237 * <DataType> <VarName>
238 * They represent the variables that are to be
239 * declared before we enter into the branch
240 * whose endOfBranch is given as argument.
241 * @param endOfBranch PC when branch ends.
242 * @return Returns a List containing Strings in the above mentioned
243 * format.
244 **/
245 public List defineVariable(int endOfBranch) {
246 List result = new Vector();
247 Enumeration enum = Collections.enumeration(symbols.values());
248 while (enum.hasMoreElements()) {
249 List list = (List) enum.nextElement();
250 for (int i = 0; i < list.size(); i++) {
251 JLocalEntry ent = (JLocalEntry) list.get(i);
252 if (!ent.isDeclared()
253 && ent.getLastReferredIndex() > endOfBranch) {
254
255 String type = imports.getClassName(
256 Helper.getJavaDataType(
257 ent.getDeclarationType(),
258 false));
259 result.add(type + " " + ent.getName());
260 ent.declareVariable();
261 }
262 }
263 }
264 return result;
265 }
266
267
268 /**
269 * Touch variable is basically assigning a new datatype
270 * in place of the old one.
271 * This arises especially in the following case:
272 * Say, we see
273 * List list = new Vector();
274 * list.add("sz");
275 * On seeing new Vector() we conclude the var type is of
276 * type Vector.
277 * On seeing list.add("sz") we 'touch' it saying that
278 * the type is now List and not Vector.
279 * @param aVarName Variable name
280 * @param aNewType New Datatype
281 **/
282 public void touchVariable(String aVarName, String aNewType) {
283 Object obj = symNames.get(aVarName);
284 if (obj != null) {
285 JLocalEntry ent = (JLocalEntry) obj;
286 if (!ent.isDeclared()) {
287 String oldType = ent.getDeclarationType();
288 if (!oldType.equals(aNewType)) {
289 String newName = genName(aNewType,
290 ent.getVarIndex());
291 ent.setName(newName);//Rename it again.
292 ent.setDeclarationType(aNewType);
293
294 //Modify the map of names again.
295 symNames.remove(aVarName);
296 symNames.put(newName, ent);
297 }
298 }
299
300 } else {
301 //Helper.log("touchVariable: DANGEROUS ");
302 }
303 }
304
305 /**
306 * @param aVarIndex Index of local variable into symbol table.
307 * @param aInsIndex Index of instruction into bytecode array
308 * of method.
309 * @return Returns a name of the variable given the
310 * variable index and the instruction index.
311 * @throws RevEngineException Thrown in case of any problem.
312 **/
313 public String getName(int aVarIndex, int aInsIndex)
314 throws RevEngineException {
315 return getMatchingEntry(aVarIndex, aInsIndex).getName();
316 }
317
318 /**
319 * @param aVarIndex Index of local variable into symbol table.
320 * @param aInsIndex Index of instruction into bytecode array
321 * of method.
322 * @return Returns a dataType of the variable given the
323 * variable index and the instruction index.
324 * @throws RevEngineException Thrown in case of any problem.
325 **/
326 public String getDataType(int aVarIndex, int aInsIndex)
327 throws RevEngineException {
328 return getMatchingEntry(aVarIndex, aInsIndex).
329 getDeclarationType();
330 }
331
332 /**
333 * Declares a variable represented by aVarIndex and
334 * aInsIndex and returns its datatype.
335 * @param aVarIndex Index of local variable into symbol table.
336 * @param aInsIndex Index of instruction into bytecode array
337 * of method.
338 * @return Returns a String containing type.
339 * @throws RevEngineException Thrown in case of any problem.
340 **/
341 public final String declare(int aVarIndex, int aInsIndex)
342 throws RevEngineException {
343 if (aVarIndex <= maxArgs) {
344 return null;
345 }
346 JLocalEntry ent = getMatchingEntry(aVarIndex, aInsIndex);
347 if (!ent.isDeclared()) {
348 String declareType = ent.getDeclarationType();
349 ent.declareVariable();
350 return imports.getClassName(
351 Helper.getJavaDataType(declareType, false));
352 } else {
353 return null;
354 }
355 }
356
357 /**
358 * @param aVarIndex Index of local variable into symbol table.
359 * @param aInsIndex Index of instruction into bytecode array
360 * of method.
361 * @return Returns a matching LocalTable entry
362 * given the variable index and the instruction index.
363 **/
364 private JLocalEntry getMatchingEntry(int aVarIndex, int aInsIndex) {
365 Object obj = symbols.get(new Integer(aVarIndex));
366 if (obj != null) {
367 List currentList = (List) obj;
368 for (int i = currentList.size() - 1; i >= 0; i--) {
369 JLocalEntry ent = (JLocalEntry) currentList.get(i);
370 if (aInsIndex >= ent.getStoreIndex()) {
371 return ent;
372 }
373 }
374 }
375 return null;
376 }
377
378
379 /**
380 * Adds a new entry to the localsymboltable.
381 * @param aVarIndex Index of local variable into symbol table.
382 * @param aStoreIndex Index when the variable is first initialized/
383 * stored.
384 * @param aDatatype Datatype of the local variable entry.
385 * @param aDeclared If the local variable mentioned in declared or not.
386 **/
387 private void addEntry(int aVarIndex, int aStoreIndex,
388 String aDatatype, boolean aDeclared) {
389
390 Helper.log("Adding entry " + aVarIndex);
391 aDeclared |= (aDatatype.indexOf("<") != -1);
392
393 Object obj = symbols.get(new Integer(aVarIndex));
394
395 List currentList;
396 if (obj == null) {
397 currentList = new Vector();
398 symbols.put(new Integer(aVarIndex), currentList);
399 } else {
400 currentList = (List) obj;
401 }
402 String name = genName(aDatatype, aVarIndex);
403 JLocalEntry ent = new JLocalEntry(
404 aVarIndex, aStoreIndex,
405 aDatatype,
406 name, aDeclared);
407 if (!currentList.contains(ent)) {
408 currentList.add(ent);
409 symNames.put(name, ent);
410 }
411 }
412
413 /**
414 * Generates an name for the type and the
415 * variableIndex.
416 * @param aType type of the variable.
417 * @param aVarIndex Variable Index to symbol table.
418 * @return Returns a name generated for the variable.
419 **/
420 private String genName(String aType, int aVarIndex) {
421 int lastArrIndex = aType.lastIndexOf("[");
422 String name;
423 boolean arrType = false;
424 if (lastArrIndex != -1) {
425 arrType = true;
426 aType = aType.substring(lastArrIndex + 1);
427 }
428 boolean basicType = Helper.isBasicType(aType);
429 aType = imports.getClassName(
430 Helper.getJavaDataType(aType, true));
431 if (basicType) {
432 name = new Character((char) (basicIndex)).toString();
433 basicIndex++;
434 } else if (aType.compareTo(THISCLASS) == 0) {
435 name = THIS;
436 } else {
437 name = aType.toLowerCase();
438 }
439 if (arrType) {
440 name += "Arr";
441 }
442 if (symNames.get(name) != null || keywords.contains(name)) {
443 name = genUniqueName(name, aVarIndex);
444 }
445 return name;
446 }
447
448 /**
449 * Generate Unique name for the variables.
450 * @param name Name of the variable.
451 * @param aVarIndex variable index for which the name is to be
452 * generated.
453 * @return Returns a new unique name in the scope of this symbol
454 * table.
455 **/
456 private String genUniqueName(String name, int aVarIndex) {
457 StringBuffer sb = new StringBuffer(name);
458 sb.append(aVarIndex);
459 return sb.toString();
460 }
461
462 /**
463 * @return Returns a list containing the keywords of the
464 * java language. All the individual members are String.
465 **/
466 public static List getKeyWordsList() {
467 List keywordList = new Vector();
468 keywordList.add("abstract");
469 keywordList.add("double");
470 keywordList.add("int");
471 keywordList.add("strictfp");
472 keywordList.add("boolean");
473 keywordList.add("else");
474 keywordList.add("interface");
475 keywordList.add("super");
476 keywordList.add("synchronized");
477 keywordList.add("break");
478 keywordList.add("extends");
479 keywordList.add("long");
480 keywordList.add("switch");
481 keywordList.add("byte");
482 keywordList.add("final");
483 keywordList.add("native");
484 keywordList.add("case");
485 keywordList.add("finally");
486 keywordList.add("new");
487 // keywordList.add("this");
488 keywordList.add("catch");
489 keywordList.add("float");
490 keywordList.add("package");
491 keywordList.add("throw");
492 keywordList.add("char");
493 keywordList.add("for");
494 keywordList.add("private");
495 keywordList.add("throws");
496 keywordList.add("class");
497 keywordList.add("goto");
498 keywordList.add("protected");
499 keywordList.add("transient");
500 keywordList.add("const");
501 keywordList.add("if");
502 keywordList.add("public");
503 keywordList.add("try");
504 keywordList.add("continue");
505 keywordList.add("implements");
506 keywordList.add("return");
507 keywordList.add("void");
508 keywordList.add("default");
509 keywordList.add("import");
510 keywordList.add("short");
511 keywordList.add("volatile");
512 keywordList.add("do");
513 keywordList.add("instanceof");
514 keywordList.add("static");
515 keywordList.add("while");
516 return keywordList;
517 }
518
519 /**
520 * @return Returns Stringified form of this class
521 **/
522 public String toString() {
523 StringBuffer sb = new StringBuffer("");
524 if (symbols != null) {
525 Iterator i1 = symbols.keySet().iterator();
526 while (i1.hasNext()) {
527 Integer k1 = (Integer) i1.next();
528 Iterator i2 = ((List) symbols.get(k1)).iterator();
529 if (i2.hasNext()) {
530 sb.append(k1 + " = ");
531 while (i2.hasNext()) {
532 sb.append(i2.next());
533 }
534 }
535 }
536 }
537 return sb.toString();
538 }
539 }