package eu.bibl.cfide.engine.decompiler; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.FieldNode; import org.objectweb.asm.tree.InnerClassNode; import org.objectweb.asm.tree.MethodNode; import eu.bibl.banalysis.asm.ClassNode; import eu.bibl.banalysis.storage.classes.ClassContainer; import eu.bibl.cfide.context.CFIDEContext; import eu.bibl.cfide.io.config.CFIDEConfig; public class ClassNodeDecompilationUnit implements DecompilationUnit<ClassNode> { public static final Map<Integer, String> VERSION_TABLE = new HashMap<Integer, String>(); static { VERSION_TABLE.put(Opcodes.V1_1, "V1_1"); VERSION_TABLE.put(Opcodes.V1_2, "V1_2"); VERSION_TABLE.put(Opcodes.V1_3, "V1_3"); VERSION_TABLE.put(Opcodes.V1_4, "V1_4"); VERSION_TABLE.put(Opcodes.V1_5, "V1_5"); VERSION_TABLE.put(Opcodes.V1_6, "V1_6"); VERSION_TABLE.put(Opcodes.V1_7, "V1_7"); VERSION_TABLE.put(Opcodes.V1_8, "V1_8"); } protected CFIDEContext context; protected DecompilationUnit<FieldNode> fndu; protected DecompilationUnit<MethodNode> mndu; public ClassNodeDecompilationUnit(CFIDEContext context) { this.context = context; fndu = getFieldNodeDecompilationUnitImpl(); mndu = getMethodNodeDecompilationUnitImpl(); } @SuppressWarnings("unchecked") protected DecompilationUnit<FieldNode> getFieldNodeDecompilationUnitImpl() { DecompilationUnit<FieldNode> fnduImpl = null; String className = null; CFIDEConfig config = context.config; try { className = config.getProperty(CFIDEConfig.DECOMPILER_FIELD_DECOMPILATION_UNIT_CLASS_KEY, FieldNodeDecompilationUnit.class.getCanonicalName()); Class<?> c = Class.forName(className); Constructor<?> c1 = c.getConstructor(CFIDEContext.class); fnduImpl = (DecompilationUnit<FieldNode>) c1.newInstance(context); if (fnduImpl == null) { fnduImpl = new FieldNodeDecompilationUnit(context); } } catch (Exception e) { System.out.println("Error loading custom DecompilationVisitor<FieldNode>: " + className); e.printStackTrace(); config.putProperty(CFIDEConfig.DECOMPILER_FIELD_DECOMPILATION_UNIT_CLASS_KEY, FieldNodeDecompilationUnit.class.getCanonicalName()); fnduImpl = new FieldNodeDecompilationUnit(context); } return fnduImpl; } @SuppressWarnings("unchecked") protected DecompilationUnit<MethodNode> getMethodNodeDecompilationUnitImpl() { DecompilationUnit<MethodNode> mnduImpl = null; String className = null; CFIDEConfig config = context.config; try { className = config.getProperty(CFIDEConfig.DECOMPILER_METHOD_DECOMPILATION_UNIT_CLASS_KEY, MethodNodeDecompilationUnit.class.getCanonicalName()); Class<?> c = Class.forName(className); Constructor<?> c1 = c.getConstructor(CFIDEContext.class); mnduImpl = (DecompilationUnit<MethodNode>) c1.newInstance(context); if (mnduImpl == null) { mnduImpl = new MethodNodeDecompilationUnit(context); } } catch (Exception e) { System.out.println("Error loading custom DecompilationVisitor<MethodNode>: " + className); e.printStackTrace(); config.putProperty(CFIDEConfig.DECOMPILER_METHOD_DECOMPILATION_UNIT_CLASS_KEY, MethodNodeDecompilationUnit.class.getCanonicalName()); mnduImpl = new MethodNodeDecompilationUnit(context); } return mnduImpl; } @Override public PrefixedStringBuilder decompile(PrefixedStringBuilder sb, ClassNode cn) { System.out.println("Decompiling: " + cn.name); buildClassNodeRepresentation(sb, cn.name, cn); return sb; } protected PrefixedStringBuilder buildClassNodeRepresentation(PrefixedStringBuilder sb, String parent, ClassNode cn) { // try { // Thread.sleep(100); // } catch (InterruptedException e) { // } ClassContainer container = context.jarDownloader.getContents(); sb.append("using asm ASM4\n"); sb.append("using ver "); sb.append(VERSION_TABLE.get(cn.version)); sb.append("\n\n"); sb.append("class: "); sb.append(getAccessString(cn.access)); sb.append(" "); sb.append(cn.name); sb.append(" extends "); sb.append(cn.superName); int amountOfInterfaces = cn.interfaces.size(); if (amountOfInterfaces > 0) { sb.append(" implements "); sb.append(cn.interfaces.get(0)); for (int i = 1; i < amountOfInterfaces; i++) { sb.append(", "); sb.append(cn.interfaces.get(i)); } } sb.append(" {\n"); if (cn.fields.size() > 0) { for (FieldNode fn : cn.fields()) { sb.append("\n"); fndu.decompile(sb, fn); } sb.append("\n"); } for (MethodNode mn : cn.methods()) { sb.append("\n"); mndu.decompile(sb, mn); } int done = 0; for (Object o : cn.innerClasses) { InnerClassNode innerClassNode = (InnerClassNode) o; String innerClassName = innerClassNode.name; String outerClassName = innerClassNode.outerName; if ((innerClassName != null)) { ClassNode cn1 = container.getNodes().get(innerClassName); if (cn1 != null) { if (((parent == null) && (outerClassName != null)) || ((outerClassName == null) && (parent != null))) { System.out.println("----------------------------------------------------"); System.out.println("Class: " + cn.name + " parent: " + parent); System.out.println("Inner: " + innerClassName + " parent: " + outerClassName); sb.appendPrefix(" "); sb.append("\n\n"); sb = buildClassNodeRepresentation(sb, outerClassName, cn1); sb.trimPrefix(5); done++; } } else { sb.appendPrefix(" "); sb.append("\n"); sb.append("NULL_INNER_CLASS: "); sb.append(innerClassName); sb.append("\n\n"); sb.trimPrefix(5); } } } if (done > 0)// not logical but due to bad code, have to add an extra new line, just for aesthetics sb.append("\n"); sb.append("} //end of "); sb.append(cn.name); // System.out.println("Wrote end for " + cn.name + " with prefix length: " + sb.prefix.length()); return sb; } public static String getAccessString(int access) { List<String> tokens = new ArrayList<String>(); if ((access & Opcodes.ACC_PUBLIC) != 0) tokens.add("public"); if ((access & Opcodes.ACC_PRIVATE) != 0) tokens.add("private"); if ((access & Opcodes.ACC_PROTECTED) != 0) tokens.add("protected"); if ((access & Opcodes.ACC_FINAL) != 0) tokens.add("final"); if ((access & Opcodes.ACC_SYNTHETIC) != 0) tokens.add("synthetic"); // if ((access & Opcodes.ACC_SUPER) != 0) // tokens.add("super"); implied by invokespecial insn if ((access & Opcodes.ACC_ABSTRACT) != 0) tokens.add("abstract"); if ((access & Opcodes.ACC_INTERFACE) != 0) tokens.add("interface"); if ((access & Opcodes.ACC_ENUM) != 0) tokens.add("enum"); if ((access & Opcodes.ACC_ANNOTATION) != 0) tokens.add("annotation"); if (!tokens.contains("interface") && !tokens.contains("enum") && !tokens.contains("annotation")) tokens.add("class"); if (tokens.size() == 0) return "[Error parsing]"; // hackery delimeters StringBuilder sb = new StringBuilder(tokens.get(0)); for (int i = 1; i < tokens.size(); i++) { sb.append(" "); sb.append(tokens.get(i)); } return sb.toString(); } }