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