blob: f0f8fa9d22f3635ea2e226b652c44c1460abe2e3 [file] [log] [blame]
import org.apache.bcel.classfile.Visitor;
import org.apache.bcel.classfile.*;
import org.apache.bcel.generic.*;
import java.io.*;
import java.util.*;
import org.apache.bcel.Constants;
import org.apache.bcel.Repository;
/**
* Disassemble Java class object into the <A HREF="http://www.cat.nyu.edu/meyer/jasmin">
* JASMIN</A> format.
*
* @version $Id$
* @author <A HREF="http://www.berlin.de/~markus.dahm/">M. Dahm</A>
*/
public class JasminVisitor extends org.apache.bcel.classfile.EmptyVisitor {
private JavaClass clazz;
private PrintWriter out;
private String class_name;
private ConstantPoolGen cp;
public JasminVisitor(JavaClass clazz, OutputStream out) {
this.clazz = clazz;
this.out = new PrintWriter(out);
class_name = clazz.getClassName();
cp = new ConstantPoolGen(clazz.getConstantPool());
}
/**
* Start traversal using DefaultVisitor pattern.
*/
public void disassemble() {
new org.apache.bcel.classfile.DescendingVisitor(clazz, this).visit();
out.close();
}
public void visitJavaClass(JavaClass clazz) {
out.println(";; Produced by JasminVisitor (BCEL)");
out.println(";; http://jakarta.apache.org/bcel/");
out.println(";; " + new Date() + "\n");
out.println(".source " + clazz.getSourceFileName());
out.println("." + Utility.classOrInterface(clazz.getAccessFlags()) + " " +
Utility.accessToString(clazz.getAccessFlags(), true) +
" " + clazz.getClassName().replace('.', '/'));
out.println(".super " + clazz.getSuperclassName().replace('.', '/'));
String[] interfaces = clazz.getInterfaceNames();
for(int i=0; i < interfaces.length; i++)
out.println(".implements " + interfaces[i].replace('.', '/'));
out.print("\n");
}
public void visitField(Field field) {
out.print(".field " + Utility.accessToString(field.getAccessFlags()) +
" " + field.getName() + " " + field.getSignature());
if(field.getAttributes().length == 0)
out.print("\n");
}
public void visitConstantValue(ConstantValue cv) {
out.println(" = " + cv);
}
private Method method;
/**
* Unfortunately Jasmin expects ".end method" after each method. Thus we've to check
* for every of the method's attributes if it's the last one and print ".end method"
* then.
*/
private final void printEndMethod(Attribute attr) {
Attribute[] attributes = method.getAttributes();
if(attr == attributes[attributes.length - 1])
out.println(".end method");
}
public void visitDeprecated(Deprecated attribute) { printEndMethod(attribute); }
public void visitSynthetic(Synthetic attribute) { printEndMethod(attribute); }
public void visitMethod(Method method) {
out.println("\n.method " + Utility.accessToString(method.getAccessFlags()) +
" " + method.getName() + method.getSignature());
this.method = method; // Remember for use in subsequent visitXXX calls
Attribute[] attributes = method.getAttributes();
if((attributes == null) || (attributes.length == 0))
out.println(".end method");
}
public void visitExceptionTable(ExceptionTable e) {
String[] names = e.getExceptionNames();
for(int i=0; i < names.length; i++)
out.println(".throws " + names[i].replace('.', '/'));
printEndMethod(e);
}
private Hashtable map;
public void visitCode(Code code) {
int label_counter = 0;
out.println(".limit stack " + code.getMaxStack());
out.println(".limit locals " + code.getMaxLocals());
MethodGen mg = new MethodGen(method, class_name, cp);
InstructionList il = mg.getInstructionList();
InstructionHandle[] ihs = il.getInstructionHandles();
/* Pass 1: Give all referenced instruction handles a symbolic name, i.e. a
* label.
*/
map = new Hashtable();
for(int i=0; i < ihs.length; i++) {
if(ihs[i] instanceof BranchHandle) {
BranchInstruction bi = (BranchInstruction)ihs[i].getInstruction();
if(bi instanceof Select) { // Special cases LOOKUPSWITCH and TABLESWITCH
InstructionHandle[] targets = ((Select)bi).getTargets();
for(int j=0; j < targets.length; j++)
put(targets[j], "Label" + label_counter++ + ":");
}
InstructionHandle ih = bi.getTarget();
put(ih, "Label" + label_counter++ + ":");
}
}
LocalVariableGen[] lvs = mg.getLocalVariables();
for(int i=0; i < lvs.length; i++) {
InstructionHandle ih = lvs[i].getStart();
put(ih, "Label" + label_counter++ + ":");
ih = lvs[i].getEnd();
put(ih, "Label" + label_counter++ + ":");
}
CodeExceptionGen[] ehs = mg.getExceptionHandlers();
for(int i=0; i < ehs.length; i++) {
CodeExceptionGen c = ehs[i];
InstructionHandle ih = c.getStartPC();
put(ih, "Label" + label_counter++ + ":");
ih = c.getEndPC();
put(ih, "Label" + label_counter++ + ":");
ih = c.getHandlerPC();
put(ih, "Label" + label_counter++ + ":");
}
LineNumberGen[] lns = mg.getLineNumbers();
for(int i=0; i < lns.length; i++) {
InstructionHandle ih = lns[i].getInstruction();
put(ih, ".line " + lns[i].getSourceLine());
}
/* Pass 2: Output code.
*/
for(int i=0; i < lvs.length; i++) {
LocalVariableGen l = lvs[i];
out.println(".var " + l.getIndex() + " is " + l.getName() + " " +
l.getType().getSignature() +
" from " + get(l.getStart()) +
" to " + get(l.getEnd()));
}
out.print("\n");
for(int i=0; i < ihs.length; i++) {
InstructionHandle ih = ihs[i];
Instruction inst = ih.getInstruction();
String str = (String)map.get(ih);
if(str != null)
out.println(str);
if(inst instanceof BranchInstruction) {
if(inst instanceof Select) { // Special cases LOOKUPSWITCH and TABLESWITCH
Select s = (Select)inst;
int[] matchs = s.getMatchs();
InstructionHandle[] targets = s.getTargets();
if(s instanceof TABLESWITCH) {
out.println("\ttableswitch " + matchs[0] + " " +
matchs[matchs.length - 1]);
for(int j=0; j < targets.length; j++)
out.println("\t\t" + get(targets[j]));
} else { // LOOKUPSWITCH
out.println("\tlookupswitch ");
for(int j=0; j < targets.length; j++)
out.println("\t\t" + matchs[j] + " : " + get(targets[j]));
}
out.println("\t\tdefault: " + get(s.getTarget())); // Applies for both
} else {
BranchInstruction bi = (BranchInstruction)inst;
ih = bi.getTarget();
str = get(ih);
out.println("\t" + Constants.OPCODE_NAMES[bi.getOpcode()] + " " + str);
}
}
else
out.println("\t" + inst.toString(cp.getConstantPool()));
}
out.print("\n");
for(int i=0; i < ehs.length; i++) {
CodeExceptionGen c = ehs[i];
ObjectType caught = c.getCatchType();
String class_name = (caught == null)? // catch any exception, used when compiling finally
"all" : caught.getClassName().replace('.', '/');
out.println(".catch " + class_name + " from " +
get(c.getStartPC()) + " to " + get(c.getEndPC()) +
" using " + get(c.getHandlerPC()));
}
printEndMethod(code);
}
private final String get(InstructionHandle ih) {
String str = new StringTokenizer((String)map.get(ih), "\n").nextToken();
return str.substring(0, str.length() - 1);
}
private final void put(InstructionHandle ih, String line) {
String str = (String)map.get(ih);
if(str == null)
map.put(ih, line);
else {
if(line.startsWith("Label") || str.endsWith(line)) // Already have a label in the map
return;
map.put(ih, str + "\n" + line); // append
}
}
public static void main(String[] argv) {
ClassParser parser=null;
JavaClass java_class;
try {
if(argv.length == 0)
System.err.println("disassemble: No input files specified");
else {
for(int i=0; i < argv.length; i++) {
if((java_class = Repository.lookupClass(argv[i])) == null)
java_class = new ClassParser(argv[i]).parse();
String class_name = java_class.getClassName();
int index = class_name.lastIndexOf('.');
String path = class_name.substring(0, index + 1).replace('.', File.separatorChar);
class_name = class_name.substring(index + 1);
if(!path.equals("")) {
File f = new File(path);
f.mkdirs();
}
String name = path + class_name + ".j";
FileOutputStream out = new FileOutputStream(name);
new JasminVisitor(java_class, out).disassemble();
System.out.println("File dumped to: " + name);
}
}
} catch(Exception e) {
e.printStackTrace();
}
}
}