-
Notifications
You must be signed in to change notification settings - Fork 8
Disassembly
A disassembly tool is provided which serves as a generic class disassembler. It also supports an output format which demonstrates how to use the Cojen API.
The disassembly tool can disassemble a class specified by a file path or by class name. For example, the standard String class can be disassembled as follows:
java org.cojen.classfile.DisassemblyTool java.lang.String
The disassembly tool supports a format option, which can be "assembly" or "builder". If not specified, it defaults to "assembly". Here is sample output generated by disassembling one of Cojen's utility classes, in assembly format:
/** * Disassembled on Thu Dec 14 08:30:00 PST 2006. * * @target 1.0 * @source BeanIntrospector.java */ public class org.cojen.util.BeanIntrospector extends java.lang.Object { /** * @synthetic */ static java.lang.Class class$java$lang$Object; private static java.util.Map cPropertiesCache; static { // max stack: 2 // max locals: 0 // line 35 new org.cojen.util.WeakIdentityMap dup invokespecial void org.cojen.util.WeakIdentityMap.<init>() putstatic java.util.Map org.cojen.util.BeanIntrospector.cPropertiesCache return } public static void main(java.lang.String[]) throws java.lang.Exception { // max stack: 3 // max locals: 1 // line 38 getstatic java.io.PrintStream java.lang.System.out aload_0 iconst_0 aaload invokestatic java.lang.Class java.lang.Class.forName(java.lang.String) invokestatic java.util.Map org.cojen.util.BeanIntrospector.getAllProperties(java.lang.Class) invokevirtual void java.io.PrintStream.println(java.lang.Object) // line 39 return } ... }
When disassembled using the builder format, a valid Java source file is generated which when compiled and run, rebuilds the original class. This demonstrates how to use Cojen, and it also allows Java classes to be modified when source code is not available.
Even if the generated class file builder is run without modification, the emitted class file will likely produce a different binary representation than the original class. This is because Cojen re-orders the class constant pool and reduces local variable usage.
java org.cojen.classfile.DisassemblyTool -f builder org.cojen.util.BeanIntrospector
import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.OutputStream; import org.cojen.classfile.ClassFile; import org.cojen.classfile.CodeBuilder; import org.cojen.classfile.FieldInfo; import org.cojen.classfile.Label; import org.cojen.classfile.LocalVariable; import org.cojen.classfile.Location; import org.cojen.classfile.MethodInfo; import org.cojen.classfile.Modifiers; import org.cojen.classfile.Opcode; import org.cojen.classfile.TypeDesc; /** * Builds ClassFile for org.cojen.util.BeanIntrospector * * @author auto-generated */ public class ClassFileBuilder { public static void main(String[] args) throws Exception { // public class org.cojen.util.BeanIntrospector ClassFile cf = createClassFile(); if (args.length > 0) { File file = new File(args[0]); if (file.isDirectory()) { writeClassFiles(cf, file); } else { OutputStream out = new BufferedOutputStream(new FileOutputStream(file)); cf.writeTo(out); out.close(); } } } private static void writeClassFiles(ClassFile cf, File dir) throws Exception { File file = new File(dir, cf.getClassName().replace('.', '/') + ".class"); file.getParentFile().mkdirs(); OutputStream out = new BufferedOutputStream(new FileOutputStream(file)); cf.writeTo(out); out.close(); ClassFile[] innerClasses = cf.getInnerClasses(); for (int i=0; i<innerClasses.length; i++) { writeClassFiles(innerClasses[i], dir); } } public static ClassFile createClassFile() { ClassFile cf = new ClassFile("org.cojen.util.BeanIntrospector", "java.lang.Object"); cf.setTarget("1.0"); cf.setSourceFile("BeanIntrospector.java"); createStaticInitializer(cf); // // Create fields // cf.addField(Modifiers.PRIVATE.toStatic(true), "cPropertiesCache", TypeDesc.forClass("java.util.Map")); FieldInfo fi = cf.addField(Modifiers.NONE.toStatic(true), "class$java$lang$Object", TypeDesc.forClass("java.lang.Class")); fi.markSynthetic(); // // Create constructors // // public void <init>() createConstructor_1(cf); // // Create methods // // public static void main(java.lang.String[]) createMethod_1(cf); // public static java.util.Map getAllProperties(java.lang.Class) createMethod_2(cf); ... return cf; } private static void createStaticInitializer(ClassFile cf) { MethodInfo mi = cf.addInitializer(); CodeBuilder b = new CodeBuilder(mi); b.mapLineNumber(35); TypeDesc type_1 = TypeDesc.forClass("org.cojen.util.WeakIdentityMap"); b.newObject(type_1); b.dup(); b.invokeConstructor("org.cojen.util.WeakIdentityMap", null); TypeDesc type_2 = TypeDesc.forClass("java.util.Map"); b.storeStaticField("cPropertiesCache", type_2); b.returnVoid(); } // public void <init>() private static void createConstructor_1(ClassFile cf) { MethodInfo mi = cf.addConstructor(Modifiers.PUBLIC, null); CodeBuilder b = new CodeBuilder(mi); b.mapLineNumber(33); b.loadThis(); b.invokeSuperConstructor(null); b.mapLineNumber(307); b.returnVoid(); } // public static void main(java.lang.String[]) private static void createMethod_1(ClassFile cf) { MethodInfo mi = cf.addMethod(Modifiers.PUBLIC_STATIC, "main", null, new TypeDesc[] {TypeDesc.STRING.toArrayType()}); mi.addException(TypeDesc.forClass("java.lang.Exception")); CodeBuilder b = new CodeBuilder(mi); LocalVariable var_1 = b.getParameter(0); b.mapLineNumber(38); TypeDesc type_1 = TypeDesc.forClass("java.io.PrintStream"); b.loadStaticField("java.lang.System", "out", type_1); b.loadLocal(var_1); b.loadConstant(0); b.loadFromArray(TypeDesc.OBJECT); TypeDesc type_2 = TypeDesc.forClass("java.lang.Class"); TypeDesc[] params_1 = new TypeDesc[] {TypeDesc.STRING}; b.invokeStatic("java.lang.Class", "forName", type_2, params_1); TypeDesc type_3 = TypeDesc.forClass("java.util.Map"); TypeDesc[] params_2 = new TypeDesc[] {type_2}; b.invokeStatic("getAllProperties", type_3, params_2); TypeDesc[] params_3 = new TypeDesc[] {TypeDesc.OBJECT}; b.invokeVirtual("java.io.PrintStream", "println", null, params_3); b.mapLineNumber(39); b.returnVoid(); } ... }