/* * Copyright 2004-2010 Brian S O'Neill * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.cojen.classfile; import java.io.PrintWriter; import org.cojen.classfile.constant.ConstantClassInfo; import org.cojen.classfile.constant.ConstantDoubleInfo; import org.cojen.classfile.constant.ConstantFieldInfo; import org.cojen.classfile.constant.ConstantFloatInfo; import org.cojen.classfile.constant.ConstantIntegerInfo; import org.cojen.classfile.constant.ConstantInterfaceMethodInfo; import org.cojen.classfile.constant.ConstantLongInfo; import org.cojen.classfile.constant.ConstantMethodInfo; import org.cojen.classfile.constant.ConstantNameAndTypeInfo; import org.cojen.classfile.constant.ConstantStringInfo; /** * Disassembles a ClassFile into a Java source file, which when run, produces * the original class. * * @author Brian S O'Neill */ class BuilderStylePrinter implements DisassemblyTool.Printer { private PrintWriter mOut; private int mIndent = 0; private boolean mNeedIndent = true; public BuilderStylePrinter() { } public void disassemble(ClassFile cf, PrintWriter out) { mOut = out; println("import java.io.BufferedOutputStream;"); println("import java.io.File;"); println("import java.io.FileOutputStream;"); println("import java.io.OutputStream;"); println(); println("import org.cojen.classfile.ClassFile;"); println("import org.cojen.classfile.CodeBuilder;"); println("import org.cojen.classfile.FieldInfo;"); println("import org.cojen.classfile.Label;"); println("import org.cojen.classfile.LocalVariable;"); println("import org.cojen.classfile.Location;"); println("import org.cojen.classfile.MethodInfo;"); println("import org.cojen.classfile.Modifiers;"); println("import org.cojen.classfile.Opcode;"); println("import org.cojen.classfile.TypeDesc;"); disassemble(cf, (String)null); } private void disassemble(ClassFile cf, String innerClassSuffix) { println(); if (innerClassSuffix == null) { println("/**"); println(" * Builds ClassFile for " + cf.getClassName()); println(" *"); println(" * @author auto-generated"); println(" */"); println("public class ClassFileBuilder {"); } else { println("/**"); println(" * Builds ClassFile for " + cf.getClassName()); println(" */"); println("private static class InnerBuilder" + innerClassSuffix + " {"); } mIndent += 4; if (innerClassSuffix == null) { println("public static void main(String[] args) throws Exception {"); mIndent += 4; println("// " + cf); println("ClassFile cf = createClassFile();"); println(); println("if (args.length > 0) {"); mIndent += 4; println("File file = new File(args[0]);"); println("if (file.isDirectory()) {"); mIndent += 4; println("writeClassFiles(cf, file);"); mIndent -= 4; println("} else {"); mIndent += 4; println("OutputStream out = new BufferedOutputStream(new FileOutputStream(file));"); println("cf.writeTo(out);"); println("out.close();"); mIndent -= 4; println("}"); mIndent -= 4; println("}"); mIndent -= 4; println("}"); println(); println("private static void writeClassFiles(ClassFile cf, File dir) throws Exception {"); mIndent += 4; println("File file = new File(dir, cf.getClassName().replace('.', '/') + \".class\");"); println("file.getParentFile().mkdirs();"); println("OutputStream out = new BufferedOutputStream(new FileOutputStream(file));"); println("cf.writeTo(out);"); println("out.close();"); println(); println("ClassFile[] innerClasses = cf.getInnerClasses();"); println("for (int i=0; i<innerClasses.length; i++) {"); mIndent += 4; println("writeClassFiles(innerClasses[i], dir);"); mIndent -= 4; println("}"); mIndent -= 4; println("}"); println(); } if (innerClassSuffix == null) { println("public static ClassFile createClassFile() {"); mIndent += 4; println("ClassFile cf = new ClassFile(\"" + escape(cf.getClassName()) + "\", \"" + escape(cf.getSuperClassName()) + "\");"); } else { println("static ClassFile createClassFile(ClassFile cf) {"); mIndent += 4; } if (cf.getTarget() != null) { println("cf.setTarget(\"" + escape(cf.getTarget()) + "\");"); } println("cf.setSourceFile(\"" + escape(cf.getSourceFile()) + "\");"); if (cf.isSynthetic()) { println("cf.markSynthetic();"); } if (cf.isDeprecated()) { println("cf.markDeprecated();"); } if (!cf.getModifiers().equals(Modifiers.PUBLIC)) { print("cf.setModifiers("); printModifiers(cf); println(");"); } String[] interfaces = cf.getInterfaces(); for (int i=0; i<interfaces.length; i++) { println("cf.addInterface(\"" + escape(interfaces[i]) + "\");"); } if (cf.getInitializer() != null) { println(); println("createStaticInitializer(cf);"); } FieldInfo[] fields = cf.getFields(); boolean createdFieldVariable = false; for (int i=0; i<fields.length; i++) { if (i == 0) { println(); println("//"); println("// Create fields"); println("//"); } println(); FieldInfo fi = fields[i]; if (fi.isSynthetic() || fi.isDeprecated() || fi.getConstantValue() != null) { if (!createdFieldVariable) { print("FieldInfo "); createdFieldVariable = true; } print("fi = "); } print("cf.addField("); printModifiers(fi); print(", "); print('\"' + escape(fi.getName()) + "\", "); print(fi.getType()); println(");"); if (fi.getConstantValue() != null) { ConstantInfo constant = fi.getConstantValue(); print("fi.setConstantValue("); if (constant instanceof ConstantStringInfo) { print("\""); String value = ((ConstantStringInfo)constant).getValue(); print(escape(value)); print("\""); } else if (constant instanceof ConstantIntegerInfo) { print(String.valueOf(((ConstantIntegerInfo)constant).getValue())); } else if (constant instanceof ConstantLongInfo) { print(String.valueOf(((ConstantLongInfo)constant).getValue())); print("L"); } else if (constant instanceof ConstantFloatInfo) { float value = ((ConstantFloatInfo)constant).getValue(); if (value != value) { print("0.0f/0.0f"); } else if (value == Float.NEGATIVE_INFINITY) { print("-1.0f/0.0f"); } else if (value == Float.POSITIVE_INFINITY) { print("1.0f/0.0f"); } else { print(String.valueOf(value)); print("f"); } } else if (constant instanceof ConstantDoubleInfo) { double value = ((ConstantDoubleInfo)constant).getValue(); if (value != value) { print("0.0d/0.0d"); } else if (value == Float.NEGATIVE_INFINITY) { print("-1.0d/0.0d"); } else if (value == Float.POSITIVE_INFINITY) { print("1.0d/0.0d"); } else { print(String.valueOf(value)); print("d"); } } println(");"); } if (fi.isSynthetic()) { println("fi.markSynthetic();"); } if (fi.isDeprecated()) { println("fi.markDeprecated();"); } } MethodInfo[] methods = cf.getConstructors(); for (int i=0; i<methods.length; i++) { if (i == 0) { println(); println("//"); println("// Create constructors"); println("//"); } println(); println("// " + methods[i]); println("createConstructor_" + (i + 1) + "(cf);"); } methods = cf.getMethods(); for (int i=0; i<methods.length; i++) { if (i == 0) { println(); println("//"); println("// Create methods"); println("//"); } println(); println("// " + methods[i]); println("createMethod_" + (i + 1) + "(cf);"); } final ClassFile[] innerClasses = cf.getInnerClasses(); for (int i=0; i<innerClasses.length; i++) { if (i == 0) { println(); println("//"); println("// Create inner classes"); println("//"); } println(); println("// " + innerClasses[i]); if (i == 0) { print("ClassFile "); } print("innerClass = "); String name = innerClasses[i].getClassName(); String innerName = innerClasses[i].getInnerClassName(); if (innerName != null) { if ((cf.getClassName() + '$' + innerName).equals(name)) { name = null; } innerName = '"' + escape(innerName) + '"'; } if (name != null) { name = '"' + escape(name) + '"'; } println("cf.addInnerClass(" + name + ", " + innerName + ", \"" + escape(innerClasses[i].getSuperClassName()) + "\");"); String suffix = "_" + (i + 1); if (innerClassSuffix != null) { suffix = innerClassSuffix + suffix; } println("InnerBuilder" + suffix + ".createClassFile(innerClass);"); } println(); println("return cf;"); mIndent -= 4; println("}"); if (cf.getInitializer() != null) { println(); println("private static void createStaticInitializer(ClassFile cf) {"); mIndent += 4; disassemble(cf.getInitializer()); mIndent -= 4; println("}"); } methods = cf.getConstructors(); for (int i=0; i<methods.length; i++) { println(); println("// " + methods[i]); println("private static void createConstructor_" + (i + 1) + "(ClassFile cf) {"); mIndent += 4; disassemble(methods[i]); mIndent -= 4; println("}"); } methods = cf.getMethods(); for (int i=0; i<methods.length; i++) { println(); println("// " + methods[i]); println("private static void createMethod_" + (i + 1) + "(ClassFile cf) {"); mIndent += 4; disassemble(methods[i]); mIndent -= 4; println("}"); } for (int i=0; i<innerClasses.length; i++) { String suffix = "_" + (i + 1); if (innerClassSuffix != null) { suffix = innerClassSuffix + suffix; } disassemble(innerClasses[i], suffix); } mIndent -= 4; println("}"); } private void disassemble(MethodInfo mi) { print("MethodInfo mi = cf.add"); if (mi.getName().equals("<clinit>")) { println("Initializer();"); } else if (mi.getName().equals("<init>")) { print("Constructor("); printModifiers(mi); print(", "); print(mi.getMethodDescriptor().getParameterTypes()); println(");"); } else { print("Method("); printModifiers(mi); print(", "); print("\"" + escape(mi.getName()) + "\", "); print(mi.getMethodDescriptor().getReturnType()); print(", "); print(mi.getMethodDescriptor().getParameterTypes()); println(");"); } if (mi.isSynthetic()) { println("mi.markSynthetic();"); } if (mi.isDeprecated()) { println("mi.markDeprecated();"); } TypeDesc[] exceptions = mi.getExceptions(); for (int j=0; j<exceptions.length; j++) { print("mi.addException("); print(exceptions[j]); println(");"); } if (mi.getCodeAttr() != null) { println("CodeBuilder b = new CodeBuilder(mi);"); println(); TypeDesc[] paramTypes = mi.getMethodDescriptor().getParameterTypes(); boolean isStatic = mi.getModifiers().isStatic(); String indentStr = generateIndent(mIndent); new CodeDisassembler(mi).disassemble (new CodeAssemblerPrinter(paramTypes, isStatic, mOut, indentStr, ";", "b.")); } } private String escape(String str) { return CodeAssemblerPrinter.escape(str); } private void printModifiers(ClassFile cf) { printModifiers(cf.getModifiers()); } private void printModifiers(FieldInfo fi) { printModifiers(fi.getModifiers()); } private void printModifiers(MethodInfo mi) { printModifiers(mi.getModifiers()); } private void printModifiers(Modifiers modifiers) { print("Modifiers."); if (modifiers.isPublic()) { if (modifiers.isAbstract()) { print("PUBLIC_ABSTRACT"); modifiers = modifiers.toAbstract(false); } else if (modifiers.isStatic()) { print("PUBLIC_STATIC"); modifiers = modifiers.toStatic(false); } else { print("PUBLIC"); } modifiers = modifiers.toPublic(false); } else if (modifiers.isProtected()) { print("PROTECTED"); modifiers = modifiers.toProtected(false); } else if (modifiers.isPrivate()) { print("PRIVATE"); modifiers = modifiers.toPrivate(false); } else { print("NONE"); } if (modifiers.isStatic()) { print(".toStatic(true)"); } if (modifiers.isFinal()) { print(".toFinal(true)"); } if (modifiers.isSynchronized()) { print(".toSynchronized(true)"); } if (modifiers.isVolatile()) { print(".toVolatile(true)"); } if (modifiers.isTransient()) { print(".toTransient(true)"); } if (modifiers.isNative()) { print(".toNative(true)"); } if (modifiers.isInterface()) { print(".toInterface(true)"); } if (modifiers.isAbstract() && !modifiers.isInterface()) { print(".toAbstract(true)"); } if (modifiers.isStrict()) { print(".toStrict(true)"); } } private void print(TypeDesc type) { if (type == null || type == TypeDesc.VOID) { print("null"); return; } if (type.isPrimitive()) { print("TypeDesc.".concat(type.getFullName().toUpperCase())); return; } else if (type == TypeDesc.OBJECT) { print("TypeDesc.OBJECT"); return; } else if (type == TypeDesc.STRING) { print("TypeDesc.STRING"); return; } TypeDesc componentType = type.getComponentType(); if (componentType != null) { print(componentType); print(".toArrayType()"); } else { print("TypeDesc.forClass(\""); print(escape(type.getRootName())); print("\")"); } } private void print(TypeDesc[] params) { if (params == null || params.length == 0) { print("null"); return; } print("new TypeDesc[] {"); for (int i=0; i<params.length; i++) { if (i > 0) { print(", "); } print(params[i]); } print("}"); } private void print(String text) { indent(); mOut.print(text); } private void println(String text) { print(text); println(); } private void println() { mOut.println(); mNeedIndent = true; } private void indent() { if (mNeedIndent) { for (int i=mIndent; --i>= 0; ) { mOut.print(' '); } mNeedIndent = false; } } private String generateIndent(int amount) { StringBuffer buf = new StringBuffer(amount); for (int i=0; i<amount; i++) { buf.append(' '); } return buf.toString(); } }