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