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