KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > de > loskutov > bco > asm > DecompiledMethod


1 package de.loskutov.bco.asm;
2
3 import java.util.ArrayList JavaDoc;
4 import java.util.BitSet JavaDoc;
5 import java.util.HashMap JavaDoc;
6 import java.util.Iterator JavaDoc;
7 import java.util.List JavaDoc;
8 import java.util.Map JavaDoc;
9
10 import org.eclipse.core.runtime.IStatus;
11 import org.objectweb.asm.Opcodes;
12 import org.objectweb.asm.Type;
13 import org.objectweb.asm.tree.LocalVariableNode;
14 import org.objectweb.asm.tree.MethodNode;
15 import org.objectweb.asm.tree.analysis.Analyzer;
16 import org.objectweb.asm.tree.analysis.AnalyzerException;
17 import org.objectweb.asm.tree.analysis.BasicValue;
18 import org.objectweb.asm.tree.analysis.Frame;
19 import org.objectweb.asm.tree.analysis.SimpleVerifier;
20 import org.objectweb.asm.tree.analysis.Value;
21
22 import de.loskutov.bco.BytecodeOutlinePlugin;
23 import de.loskutov.bco.preferences.BCOConstants;
24
25 /**
26  * @author Eric Bruneton
27  */

28
29 public class DecompiledMethod {
30
31     private List JavaDoc text;
32
33     private List JavaDoc localVariables;
34
35     private Map JavaDoc sourceLines; // decompiled line -> source line
36

37     private Map JavaDoc decompiledLines; // source line -> decompiled line
38

39     private Map JavaDoc insns; // decompiled line -> insn
40

41     private Map JavaDoc opcodes; // decompiled line -> opcode
42

43     private Map JavaDoc insnLines; // insn -> decompile line
44

45     private int lineCount;
46
47     private int firstSourceLine; // first source line, if any
48

49     private MethodNode meth;
50
51     private Frame[] frames;
52
53     private String JavaDoc error;
54
55     private int errorInsn;
56
57     public DecompiledMethod(final String JavaDoc owner, final List JavaDoc inputText,
58         final Map JavaDoc lineNumbers, final MethodNode meth, final ClassLoader JavaDoc cl, BitSet JavaDoc modes) {
59         this.text = new ArrayList JavaDoc();
60         this.localVariables = meth.localVariables;
61         this.sourceLines = new HashMap JavaDoc();
62         this.decompiledLines = new HashMap JavaDoc();
63         this.insns = new HashMap JavaDoc();
64         this.opcodes = new HashMap JavaDoc();
65         this.insnLines = new HashMap JavaDoc();
66
67         this.meth = meth;
68         formatText(inputText, new HashMap JavaDoc(), new StringBuffer JavaDoc(), this.text);
69         computeMaps(lineNumbers);
70
71         if (modes.get(BCOConstants.F_SHOW_ANALYZER)
72             && (meth.access & Opcodes.ACC_ABSTRACT) == 0) {
73             analyzeMethod(owner, cl);
74         }
75     }
76
77     public boolean hasSourceLinesInfo(){
78         return ! sourceLines.isEmpty();
79     }
80
81     public boolean hasLocalVariablesInfo(){
82         return ! localVariables.isEmpty();
83     }
84
85     public String JavaDoc getSignature(){
86         return meth.name + meth.desc;
87     }
88
89     /**
90      * @param owner
91      * @param meth
92      * @param cl
93      */

94     private void analyzeMethod(final String JavaDoc owner, final ClassLoader JavaDoc cl) {
95         Analyzer a = new Analyzer(new SimpleVerifier() {
96
97             protected Class JavaDoc getClass(final Type t) {
98                 try {
99                     if (t.getSort() == Type.ARRAY) {
100                         return Class.forName(t.getDescriptor().replace(
101                             '/', '.'), true, cl);
102                     }
103                     return cl.loadClass(t.getClassName());
104                 } catch (ClassNotFoundException JavaDoc e) {
105                     throw new RuntimeException JavaDoc(e.toString()+" " +cl, e);
106                 }
107             }
108         });
109         try {
110             a.analyze(owner, meth);
111         } catch (AnalyzerException e) {
112             error = e.getMessage();
113             if (error.startsWith("Error at instruction ")) {
114                 error = error.substring("Error at instruction ".length());
115                 errorInsn = Integer.parseInt(error.substring(0, error
116                     .indexOf(':')));
117                 error = error.substring(error.indexOf(':') + 2);
118             } else {
119                 BytecodeOutlinePlugin.log(e, IStatus.ERROR);
120                 error = null;
121             }
122         }
123         frames = a.getFrames();
124     }
125
126     private void formatText(final List JavaDoc input, final Map JavaDoc locals, StringBuffer JavaDoc line,
127         final List JavaDoc result) {
128         for (int i = 0; i < input.size(); ++i) {
129             Object JavaDoc o = input.get(i);
130             if (o instanceof List JavaDoc) {
131                 formatText((List JavaDoc) o, locals, line, result);
132             } else if (o instanceof Index) {
133                 result.add(o);
134                 updateLocals((Index) o, locals);
135             } else if (o instanceof Integer JavaDoc) {
136                 String JavaDoc localVariableName = (String JavaDoc) locals.get(o);
137                 if (localVariableName == null) {
138                     Index index = getNextIndex(input, i);
139                     if(index != null){
140                         updateLocals(index, locals);
141                         localVariableName = (String JavaDoc) locals.get(o);
142                     }
143                 }
144                 if(localVariableName != null) {
145                     line.append(": ").append(localVariableName);
146                 }
147             } else {
148                 String JavaDoc s = o.toString();
149                 int p;
150                 do {
151                     p = s.indexOf('\n');
152                     if (p == -1) {
153                         line.append(s);
154                     } else {
155                         result.add(line.toString() + s.substring(0, p + 1));
156                         s = s.substring(p + 1);
157                         line.setLength(0);
158                     }
159                 } while (p != -1);
160             }
161         }
162     }
163
164     /**
165      * @param i
166      * @param input
167      * @return
168      */

169     private Index getNextIndex(List JavaDoc input, int startOffset) {
170         for (int i = startOffset + 1; i < input.size(); i++) {
171             Object JavaDoc object = input.get(i);
172             if(object instanceof Index){
173                 return (Index)object;
174             }
175         }
176         return null;
177     }
178
179     private void updateLocals(final Index index, final Map JavaDoc locals) {
180         for (int i = 0; i < localVariables.size(); ++i) {
181             LocalVariableNode lvNode = (LocalVariableNode) localVariables.get(i);
182             if (lvNode.start == index.labelNode) {
183                 locals.put(new Integer JavaDoc(lvNode.index), lvNode.name);
184             } else if (lvNode.end == index.labelNode) {
185                 locals.remove(new Integer JavaDoc(lvNode.index));
186             }
187         }
188     }
189
190     private void computeMaps(final Map JavaDoc lineNumbers) {
191         int currentSourceLine = -1;
192         int currentDecompiledLine = 0;
193         int currentInsn = -1;
194         int currentOpcode = -1;
195         int firstLine = -1;
196         for (int i = 0; i < text.size(); ++i) {
197             Object JavaDoc o = text.get(i);
198             if (o instanceof Index) {
199                 Index index = (Index) o;
200                 Integer JavaDoc sourceLine = null;
201                 if(index.labelNode != null) {
202                     sourceLine = (Integer JavaDoc) lineNumbers.get(index.labelNode.getLabel());
203                 }
204                 if (sourceLine != null) {
205                     currentSourceLine = sourceLine.intValue();
206                     if(firstLine == -1 || currentSourceLine < firstLine){
207                         firstLine = currentSourceLine;
208                     }
209                 }
210                 currentInsn = index.insn;
211                 currentOpcode = index.opcode;
212             } else {
213                 ++currentDecompiledLine;
214             }
215             Integer JavaDoc csl = new Integer JavaDoc(currentSourceLine);
216             Integer JavaDoc cdl = new Integer JavaDoc(currentDecompiledLine);
217             Integer JavaDoc ci = new Integer JavaDoc(currentInsn);
218             Integer JavaDoc co = new Integer JavaDoc(currentOpcode);
219             sourceLines.put(cdl, csl);
220             if (decompiledLines.get(csl) == null) {
221                 decompiledLines.put(csl, cdl);
222             }
223             insns.put(cdl, ci);
224             opcodes.put(cdl, co);
225             if (insnLines.get(ci) == null) {
226                 insnLines.put(ci, cdl);
227             }
228         }
229         lineCount = currentDecompiledLine;
230         firstSourceLine = firstLine;
231     }
232
233     public String JavaDoc getText() {
234         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
235         for (int i = 0; i < text.size(); ++i) {
236             Object JavaDoc o = text.get(i);
237             if (!(o instanceof Index)) {
238                 buf.append((String JavaDoc) o);
239             }
240         }
241         return buf.toString();
242     }
243
244     public String JavaDoc[][] getTextTable() {
245         Frame frame = null;
246         String JavaDoc error1 = "";
247         List JavaDoc lines = new ArrayList JavaDoc();
248         for (int i = 0; i < text.size(); ++i) {
249             Object JavaDoc o = text.get(i);
250             if (o instanceof Index) {
251                 if (frames != null) {
252                     frame = frames[((Index) o).insn];
253                     if (this.error != null && ((Index) o).insn == this.errorInsn) {
254                       error1 = this.error;
255                     }
256                 }
257             } else {
258                 String JavaDoc locals = " ";
259                 String JavaDoc stack = " ";
260                 if (frame != null) {
261                     StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
262                     appendFrame(buf, frame);
263                     int p = buf.indexOf(" ");
264                     locals = buf.substring(0, p);
265                     if("".equals(locals)){
266                         locals = " ";
267                     }
268                     stack = buf.substring(p + 1);
269                     if("".equals(stack)){
270                         stack = " ";
271                     }
272                 }
273                 lines.add(new String JavaDoc[]{locals, stack, o.toString(), error1});
274                 frame = null;
275                 error1 = "";
276             }
277         }
278         return (String JavaDoc[][]) lines.toArray(new String JavaDoc[lines.size()][]);
279     }
280
281     public int getLineCount() {
282         return lineCount;
283     }
284
285     public String JavaDoc getError() {
286         return error;
287     }
288
289     public int getErrorLine() {
290         if (error == null) {
291             return -1;
292         }
293         Integer JavaDoc i = (Integer JavaDoc) insnLines.get(new Integer JavaDoc(errorInsn));
294         return i == null
295             ? -1
296             : i.intValue();
297     }
298
299     private void appendFrame(final StringBuffer JavaDoc buf, final Frame f) {
300         try {
301             for (int i = 0; i < f.getLocals(); ++i) {
302                 appendValue(buf, f.getLocal(i));
303             }
304             buf.append(' ');
305             for (int i = 0; i < f.getStackSize(); ++i) {
306                 appendValue(buf, f.getStack(i));
307             }
308         } catch (IndexOutOfBoundsException JavaDoc e) {
309             // TODO should we keep this?
310
BytecodeOutlinePlugin.log(e, IStatus.WARNING);
311         }
312     }
313
314     private void appendValue(final StringBuffer JavaDoc buf, final Value v) {
315         if (((BasicValue) v).isReference()) {
316             buf.append("R");
317         } else {
318             buf.append(v.toString());
319         }
320     }
321
322     public int getFirstSourceLine(){
323         return firstSourceLine;
324     }
325
326     public int getSourceLine(final int decompiledLine) {
327         Integer JavaDoc i = (Integer JavaDoc) sourceLines.get(new Integer JavaDoc(decompiledLine));
328         return i == null
329             ? -1
330             : i.intValue();
331     }
332
333     /**
334      *
335      * @param decompiledLine
336      * @return array with two elements, first is the local variables table,
337      * second is the operands stack content. "null" value could be returned too.
338      */

339     public String JavaDoc[] getFrame(final int decompiledLine, final boolean useQualifiedNames) {
340         Integer JavaDoc insn = getBytecodeOffset(decompiledLine);
341         if (error != null && insn != null && insn.intValue() == errorInsn) {
342             return new String JavaDoc [] {error,error};
343         }
344         if (frames != null && insn != null) {
345             Frame f = frames[insn.intValue()];
346             if (f == null) {
347                 return null;
348             }
349
350             try {
351                 StringBuffer JavaDoc localsBuf = new StringBuffer JavaDoc();
352
353                 for (int i = 0; i < f.getLocals(); ++i) {
354                     String JavaDoc s = f.getLocal(i).toString();
355                     appendTypeName(i, useQualifiedNames, localsBuf, s);
356
357                     for (Iterator JavaDoc it = localVariables.iterator(); it.hasNext();) {
358                         LocalVariableNode lvnode = (LocalVariableNode) it.next();
359                         int n = lvnode.index;
360                         if( n==i) {
361                           localsBuf.append( " : ").append( lvnode.name);
362                         }
363                     }
364
365                     localsBuf.append('\n');
366                 }
367                 StringBuffer JavaDoc stackBuf = new StringBuffer JavaDoc();
368                 for (int i = 0; i < f.getStackSize(); ++i) {
369                     String JavaDoc s = f.getStack(i).toString();
370                     appendTypeName(i, useQualifiedNames, stackBuf, s);
371                     stackBuf.append('\n');
372                 }
373                 return new String JavaDoc[] {localsBuf.toString(), stackBuf.toString()};
374             } catch (IndexOutOfBoundsException JavaDoc e) {
375                 // TODO should we keep this?
376
BytecodeOutlinePlugin.log(e, IStatus.WARNING);
377             }
378         }
379         return null;
380     }
381
382     /**
383      * @param decompiledLine
384      * @return
385      */

386     public Integer JavaDoc getBytecodeOffset(final int decompiledLine) {
387         Integer JavaDoc insn = (Integer JavaDoc) insns.get(new Integer JavaDoc(decompiledLine));
388         return insn;
389     }
390
391     /**
392      * @param decompiledLine
393      * @return
394      */

395     public Integer JavaDoc getBytecodeInsn(final int decompiledLine) {
396         Integer JavaDoc insn = (Integer JavaDoc) opcodes.get(new Integer JavaDoc(decompiledLine));
397         return insn;
398     }
399
400     public String JavaDoc[][][] getFrameTables(final int decompiledLine, boolean useQualifiedNames) {
401       Integer JavaDoc insn = getBytecodeOffset(decompiledLine);
402       if (error != null && insn != null && insn.intValue() == errorInsn) {
403           return null;
404       }
405       if (frames != null && insn != null && insn.intValue() >= 0
406           && insn.intValue() < frames.length) {
407         Frame f = frames[insn.intValue()];
408         if (f == null) {
409             return null;
410         }
411
412         try {
413             ArrayList JavaDoc locals = new ArrayList JavaDoc();
414             for (int i = 0; i < f.getLocals(); ++i) {
415                 String JavaDoc varName = "";
416                 for (Iterator JavaDoc it = localVariables.iterator(); it.hasNext();) {
417                     LocalVariableNode lvnode = (LocalVariableNode) it.next();
418                     int n = lvnode.index;
419                     if( n==i) {
420                       varName = lvnode.name;
421                       // TODO take into account variable scope!
422
break;
423                     }
424                 }
425
426                 locals.add( new String JavaDoc[] {
427                     ""+i,
428                     getTypeName( useQualifiedNames, f.getLocal(i).toString()),
429                     varName});
430             }
431
432             ArrayList JavaDoc stack = new ArrayList JavaDoc();
433             for (int i = 0; i < f.getStackSize(); ++i) {
434                 stack.add( new String JavaDoc[] {
435                     ""+i,
436                     getTypeName( useQualifiedNames, f.getStack(i).toString())});
437             }
438             return new String JavaDoc[][][] {
439                 (String JavaDoc[][]) locals.toArray( new String JavaDoc[ 3][]),
440                 (String JavaDoc[][]) stack.toArray( new String JavaDoc[ 2][])};
441         } catch (IndexOutOfBoundsException JavaDoc e) {
442             // TODO should we keep this?
443
BytecodeOutlinePlugin.log(e, IStatus.ERROR);
444         }
445       }
446       return null;
447     }
448
449
450     /**
451      * Appends full type name or only simply name, depends on boolean flag.
452      *
453      * @param useQualifiedNames if false, then e.g. "Object" will be appended to
454      * buffer instead of "Ljava/lang/Object;" etc
455      * @param buf buffer to append
456      * @param s string with bytecode type name, like "Ljava/lang/Object;"
457      */

458     private void appendTypeName(int n, final boolean useQualifiedNames, StringBuffer JavaDoc buf, String JavaDoc s) {
459         buf.append(n).append( " ");
460         if(!useQualifiedNames) {
461             int idx = s.lastIndexOf('/');
462             if(idx > 0){
463                 // from "Ljava/lang/Object;" to "Object"
464
buf.append(s.substring(idx + 1, s.length() - 1));
465                 return;
466             }
467         }
468         if("Lnull;".equals(s)){
469             buf.append("null");
470         } else {
471             buf.append(s);
472         }
473     }
474
475     private String JavaDoc getTypeName(final boolean useQualifiedNames, String JavaDoc s) {
476       if (!useQualifiedNames) {
477           // get leading array symbols
478
String JavaDoc arraySymbols = "";
479           while (s.startsWith("[")){
480               arraySymbols += "[";
481               s = s.substring(1);
482           }
483
484           int idx = s.lastIndexOf('/');
485           if (idx > 0) {
486               // from "Ljava/lang/Object;" to "Object"
487
return arraySymbols + s.substring(idx + 1, s.length() - 1);
488           }
489           // this is the case on LVT view - ignore it
490
if("." == s){
491               return arraySymbols + s;
492           }
493           // resolve primitive types
494
return arraySymbols +
495               CommentedClassVisitor.getSimpleName(Type.getType(s));
496       }
497       return "Lnull;".equals(s) ? "null" : s;
498     }
499
500     public int getDecompiledLine(final int sourceLine) {
501         Integer JavaDoc i = (Integer JavaDoc) decompiledLines.get(new Integer JavaDoc(sourceLine));
502         return i == null
503             ? -1
504             : i.intValue();
505     }
506 }
507
Popular Tags