1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  *
17  */
18 
19 import java.io.File;
20 import java.io.FileOutputStream;
21 import java.io.OutputStream;
22 import java.io.PrintWriter;
23 import java.util.Date;
24 import java.util.Hashtable;
25 import java.util.StringTokenizer;
26 
27 import org.apache.bcel.Constants;
28 import org.apache.bcel.Repository;
29 import org.apache.bcel.classfile.Attribute;
30 import org.apache.bcel.classfile.ClassParser;
31 import org.apache.bcel.classfile.Code;
32 import org.apache.bcel.classfile.ConstantValue;
33 import org.apache.bcel.classfile.Deprecated;
34 import org.apache.bcel.classfile.ExceptionTable;
35 import org.apache.bcel.classfile.Field;
36 import org.apache.bcel.classfile.JavaClass;
37 import org.apache.bcel.classfile.Method;
38 import org.apache.bcel.classfile.Synthetic;
39 import org.apache.bcel.classfile.Utility;
40 import org.apache.bcel.generic.BranchHandle;
41 import org.apache.bcel.generic.BranchInstruction;
42 import org.apache.bcel.generic.CodeExceptionGen;
43 import org.apache.bcel.generic.ConstantPoolGen;
44 import org.apache.bcel.generic.Instruction;
45 import org.apache.bcel.generic.InstructionHandle;
46 import org.apache.bcel.generic.InstructionList;
47 import org.apache.bcel.generic.LineNumberGen;
48 import org.apache.bcel.generic.LocalVariableGen;
49 import org.apache.bcel.generic.MethodGen;
50 import org.apache.bcel.generic.ObjectType;
51 import org.apache.bcel.generic.Select;
52 import org.apache.bcel.generic.TABLESWITCH;
53 
54 /**
55  * Disassemble Java class object into the <a href="http://jasmin.sourceforge.net">
56  * Jasmin</a> format.
57  *
58  * @version $Id$
59  */
60 public class JasminVisitor extends org.apache.bcel.classfile.EmptyVisitor {
61     private JavaClass clazz;
62     private PrintWriter out;
63     private String class_name;
64     private ConstantPoolGen cp;
65 
JasminVisitor(JavaClass clazz, OutputStream out)66     public JasminVisitor(JavaClass clazz, OutputStream out) {
67         this.clazz = clazz;
68         this.out = new PrintWriter(out);
69         this.class_name = clazz.getClassName();
70         this.cp = new ConstantPoolGen(clazz.getConstantPool());
71     }
72 
73     /**
74      * Start traversal using DefaultVisitor pattern.
75      */
disassemble()76     public void disassemble() {
77         new org.apache.bcel.classfile.DescendingVisitor(clazz, this).visit();
78         out.close();
79     }
80 
81     @Override
visitJavaClass(JavaClass clazz)82     public void visitJavaClass(JavaClass clazz) {
83         out.println(";; Produced by JasminVisitor (BCEL)");
84         out.println(";; http://commons.apache.org/bcel/");
85         out.println(";; " + new Date() + "\n");
86 
87         out.println(".source " + clazz.getSourceFileName());
88         out.println("." + Utility.classOrInterface(clazz.getAccessFlags()) + " " +
89                 Utility.accessToString(clazz.getAccessFlags(), true) +
90                 " " + clazz.getClassName().replace('.', '/'));
91         out.println(".super " + clazz.getSuperclassName().replace('.', '/'));
92 
93         for (String iface : clazz.getInterfaceNames()) {
94             out.println(".implements " + iface.replace('.', '/'));
95         }
96 
97         out.print("\n");
98     }
99 
100     @Override
visitField(Field field)101     public void visitField(Field field) {
102         out.print(".field " + Utility.accessToString(field.getAccessFlags()) +
103                 " \"" + field.getName() + "\"" + field.getSignature());
104         if (field.getAttributes().length == 0) {
105             out.print("\n");
106         }
107     }
108 
109     @Override
visitConstantValue(ConstantValue cv)110     public void visitConstantValue(ConstantValue cv) {
111         out.println(" = " + cv);
112     }
113 
114     private Method _method;
115 
116     /**
117      * Unfortunately Jasmin expects ".end method" after each method. Thus we've to check
118      * for every of the method's attributes if it's the last one and print ".end method"
119      * then.
120      */
printEndMethod(Attribute attr)121     private void printEndMethod(Attribute attr) {
122         Attribute[] attributes = _method.getAttributes();
123 
124         if (attr == attributes[attributes.length - 1]) {
125             out.println(".end method");
126         }
127     }
128 
129     @Override
visitDeprecated(Deprecated attribute)130     public void visitDeprecated(Deprecated attribute) {
131         printEndMethod(attribute);
132     }
133 
134     @Override
visitSynthetic(Synthetic attribute)135     public void visitSynthetic(Synthetic attribute) {
136         if (_method != null) {
137             printEndMethod(attribute);
138         }
139     }
140 
141     @Override
visitMethod(Method method)142     public void visitMethod(Method method) {
143         this._method = method; // Remember for use in subsequent visitXXX calls
144 
145         out.println("\n.method " + Utility.accessToString(_method.getAccessFlags()) +
146                 " " + _method.getName() + _method.getSignature());
147 
148         Attribute[] attributes = _method.getAttributes();
149         if ((attributes == null) || (attributes.length == 0)) {
150             out.println(".end method");
151         }
152     }
153 
154     @Override
visitExceptionTable(ExceptionTable e)155     public void visitExceptionTable(ExceptionTable e) {
156         for (String name : e.getExceptionNames()) {
157             out.println(".throws " + name.replace('.', '/'));
158         }
159 
160         printEndMethod(e);
161     }
162 
163     private Hashtable<InstructionHandle, String> map;
164 
165     @Override
visitCode(Code code)166     public void visitCode(Code code) {
167         int label_counter = 0;
168 
169         out.println(".limit stack " + code.getMaxStack());
170         out.println(".limit locals " + code.getMaxLocals());
171 
172         MethodGen mg = new MethodGen(_method, class_name, cp);
173         InstructionList il = mg.getInstructionList();
174         InstructionHandle[] ihs = il.getInstructionHandles();
175 
176     /* Pass 1: Give all referenced instruction handles a symbolic name, i.e. a
177      * label.
178      */
179         map = new Hashtable<InstructionHandle, String>();
180 
181         for (InstructionHandle ih1 : ihs) {
182             if (ih1 instanceof BranchHandle) {
183                 BranchInstruction bi = (BranchInstruction) ih1.getInstruction();
184 
185                 if (bi instanceof Select) { // Special cases LOOKUPSWITCH and TABLESWITCH
186                     for (InstructionHandle target : ((Select) bi).getTargets()) {
187                         put(target, "Label" + label_counter++ + ":");
188                     }
189                 }
190 
191                 InstructionHandle ih = bi.getTarget();
192                 put(ih, "Label" + label_counter++ + ":");
193             }
194         }
195 
196         LocalVariableGen[] lvs = mg.getLocalVariables();
197         for (LocalVariableGen lv : lvs) {
198             InstructionHandle ih = lv.getStart();
199             put(ih, "Label" + label_counter++ + ":");
200             ih = lv.getEnd();
201             put(ih, "Label" + label_counter++ + ":");
202         }
203 
204         CodeExceptionGen[] ehs = mg.getExceptionHandlers();
205         for (CodeExceptionGen c : ehs) {
206             InstructionHandle ih = c.getStartPC();
207 
208             put(ih, "Label" + label_counter++ + ":");
209             ih = c.getEndPC();
210             put(ih, "Label" + label_counter++ + ":");
211             ih = c.getHandlerPC();
212             put(ih, "Label" + label_counter++ + ":");
213         }
214 
215         LineNumberGen[] lns = mg.getLineNumbers();
216         for (LineNumberGen ln : lns) {
217             InstructionHandle ih = ln.getInstruction();
218             put(ih, ".line " + ln.getSourceLine());
219         }
220 
221         // Pass 2: Output code.
222         for (LocalVariableGen l : lvs) {
223             out.println(".var " + l.getIndex() + " is " + l.getName() + " " +
224                     l.getType().getSignature() +
225                     " from " + get(l.getStart()) +
226                     " to " + get(l.getEnd()));
227         }
228 
229         out.print("\n");
230 
231         for (InstructionHandle ih : ihs) {
232             Instruction inst = ih.getInstruction();
233             String str = map.get(ih);
234 
235             if (str != null) {
236                 out.println(str);
237             }
238 
239             if (inst instanceof BranchInstruction) {
240                 if (inst instanceof Select) { // Special cases LOOKUPSWITCH and TABLESWITCH
241                     Select s = (Select) inst;
242                     int[] matchs = s.getMatchs();
243                     InstructionHandle[] targets = s.getTargets();
244 
245                     if (s instanceof TABLESWITCH) {
246                         out.println("\ttableswitch " + matchs[0] + " " + matchs[matchs.length - 1]);
247 
248                         for (InstructionHandle target : targets) {
249                             out.println("\t\t" + get(target));
250                         }
251 
252                     } else { // LOOKUPSWITCH
253                         out.println("\tlookupswitch ");
254 
255                         for (int j = 0; j < targets.length; j++) {
256                             out.println("\t\t" + matchs[j] + " : " + get(targets[j]));
257                         }
258                     }
259 
260                     out.println("\t\tdefault: " + get(s.getTarget())); // Applies for both
261                 } else {
262                     BranchInstruction bi = (BranchInstruction) inst;
263                     ih = bi.getTarget();
264                     str = get(ih);
265                     out.println("\t" + Constants.OPCODE_NAMES[bi.getOpcode()] + " " + str);
266                 }
267             } else {
268                 out.println("\t" + inst.toString(cp.getConstantPool()));
269             }
270         }
271 
272         out.print("\n");
273 
274         for (CodeExceptionGen c : ehs) {
275             ObjectType caught = c.getCatchType();
276             String class_name = (caught == null) ?  // catch any exception, used when compiling finally
277                     "all" : caught.getClassName().replace('.', '/');
278 
279             out.println(".catch " + class_name + " from " +
280                     get(c.getStartPC()) + " to " + get(c.getEndPC()) +
281                     " using " + get(c.getHandlerPC()));
282         }
283 
284         printEndMethod(code);
285     }
286 
get(InstructionHandle ih)287     private String get(InstructionHandle ih) {
288         String str = new StringTokenizer(map.get(ih), "\n").nextToken();
289         return str.substring(0, str.length() - 1);
290     }
291 
put(InstructionHandle ih, String line)292     private void put(InstructionHandle ih, String line) {
293         String str = map.get(ih);
294 
295         if (str == null) {
296             map.put(ih, line);
297         } else {
298             if (line.startsWith("Label") || str.endsWith(line)) {
299                 return;
300             }
301 
302             map.put(ih, str + "\n" + line); // append
303         }
304     }
305 
main(String[] argv)306     public static void main(String[] argv) throws Exception {
307         JavaClass java_class;
308 
309         if (argv.length == 0) {
310             System.err.println("disassemble: No input files specified");
311             return;
312         }
313 
314         for (String arg : argv) {
315             if ((java_class = Repository.lookupClass(arg)) == null) {
316                 java_class = new ClassParser(arg).parse();
317             }
318 
319             String class_name = java_class.getClassName();
320             int index = class_name.lastIndexOf('.');
321             String path = class_name.substring(0, index + 1).replace('.', File.separatorChar);
322             class_name = class_name.substring(index + 1);
323 
324             if (!path.equals("")) {
325                 File f = new File(path);
326                 f.mkdirs();
327             }
328 
329             String name = path + class_name + ".j";
330             FileOutputStream out = new FileOutputStream(name);
331             new JasminVisitor(java_class, out).disassemble();
332             System.out.println("File dumped to: " + name);
333         }
334     }
335 }
336