package bytecode; import installer.ProgressDialog; import java.io.InputStream; import java.io.PrintStream; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.WeakHashMap; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.Handle; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.TypePath; public class Bytecode2Text { static final boolean TRIM = false; public static void main(String[] args) { try { go(System.in, System.out, null); } catch(Throwable t) { t.printStackTrace(); System.exit(1); } } public static void go(InputStream inBase, PrintStream out, ProgressDialog dlg) throws Exception { try (ZipInputStream in = new ZipInputStream(inBase)) { ZipEntry ze; while((ze = in.getNextEntry()) != null) { if(!ze.getName().endsWith(".class")) { in.closeEntry(); continue; } if(dlg != null) dlg.incrementProgress(1); out.println("FILE "+ze.getName()); new ClassReader(in).accept(new TranslateClassVisitor(out), 0); } } out.close(); } private static List<Object> objectStack = new ArrayList<>(); private static WeakHashMap<Object, Boolean> seen = new WeakHashMap<>(); private static HashMap<Label, Integer> labelIDs = new HashMap<>(); private static String getObjectID(Object object) { if(object instanceof Label) { Integer id = labelIDs.get(object); if(id == null) { labelIDs.put((Label)object, id = labelIDs.size()); return "NEWLABEL"+id; } return "LABEL"+id; } if(objectStack.size() > 0) { if(objectStack.get(objectStack.size() - 1) == object) return "TOP"; if(objectStack.size() > 1 && objectStack.get(objectStack.size() - 2) == object) { objectStack.remove(objectStack.size() - 1); return "POP"; } } if(seen.put(object, false) != null) throw new RuntimeException(object+" seen before, not on stack top"); objectStack.add(object); return "PUSH"; //Integer objectID = objectIDs.get(object); //if(objectID == null) objectIDs.put(object, objectID = nextObjectID++); //return objectID; } static void resetLabelIDs() { labelIDs.clear(); } private static void logArg(PrintStream out, Object arg) { if(arg instanceof String) { try { out.println("S"+((String)arg).length()+" "+URLEncoder.encode((String)arg, "UTF-8")); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } return; } if(arg instanceof Integer) { out.println("I"+arg); return; } if(arg instanceof Object[]) { out.println("A"+((Object[])arg).length+" "+arg.getClass().getName()); for(Object o : (Object[])arg) logArg(out, o); return; } if(arg instanceof int[]) { out.println("A"+((int[])arg).length+" [I"); for(int o : (int[])arg) logArg(out, o); return; } if(arg == null) { out.println("_null"); return; } if(arg instanceof Boolean) { out.println((Boolean)arg ? "_true" : "_false"); return; } if(arg instanceof Double) { out.println("D" + Double.doubleToRawLongBits((Double)arg)); return; } if(arg instanceof Float) { out.println("F" + Float.floatToRawIntBits((Float)arg)); return; } if(arg instanceof Type) { out.println("T" + ((Type)arg).getDescriptor()); return; } if(arg instanceof Long) { out.println("J" + ((Long)arg)); return; } if(arg instanceof Label) { out.println("L" + getObjectID(arg)); return; } throw new RuntimeException(arg+" "+arg.getClass().getName()); } static void logVoid(PrintStream out, Object object, String methodName, Object... args) { out.println("CALL "+getObjectID(object)+" "+methodName+" X "+args.length); for(Object arg : args) logArg(out, arg); } static <T> T log(PrintStream out, Object object, String methodName, T _return, Object... args) { if(_return == null) throw new NullPointerException("_return"); out.println("CALL "+getObjectID(object)+" "+methodName+" "+getObjectID(_return)+" "+args.length); for(Object arg : args) logArg(out, arg); return _return; } private static class TranslateAnnotationVisitor extends AnnotationVisitor { private PrintStream out; public TranslateAnnotationVisitor(PrintStream out) { super(Opcodes.ASM5); this.out = out; } @Override public void visit(String name, Object value) { logVoid(out, this, "visit", name, value); } @Override public AnnotationVisitor visitAnnotation(String name, String desc) { return log(out, this, "visitAnnotation", new TranslateAnnotationVisitor(out), name, desc); } @Override public AnnotationVisitor visitArray(String name) { return log(out, this, "visitArray", new TranslateAnnotationVisitor(out), name); } @Override public void visitEnd() { logVoid(out, this, "visitEnd"); } @Override public void visitEnum(String name, String desc, String value) { logVoid(out, this, "visitEnum", name, desc, value); } } private static class TranslateFieldVisitor extends FieldVisitor { private PrintStream out; public TranslateFieldVisitor(PrintStream out) { super(Opcodes.ASM5); this.out = out; } @Override public AnnotationVisitor visitAnnotation(String desc, boolean visible) { return log(out, this, "visitAnnotation", new TranslateAnnotationVisitor(out), desc, visible); } @Override public void visitEnd() { logVoid(out, this, "visitEnd"); } @Override public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) { return log(out, this, "visitTypeAnnotation", new TranslateAnnotationVisitor(out), typeRef, typePath, desc, visible); } } private static class TranslateMethodVisitor extends MethodVisitor { private PrintStream out; public TranslateMethodVisitor(PrintStream out) { super(Opcodes.ASM5); this.out = out; } @Override public AnnotationVisitor visitAnnotation(String desc, boolean visible) { return log(out, this, "visitAnnotation", new TranslateAnnotationVisitor(out), desc, visible); } @Override public AnnotationVisitor visitAnnotationDefault() { return log(out, this, "visitAnnotationDefault", new TranslateAnnotationVisitor(out)); } @Override public void visitCode() { logVoid(out, this, "visitCode"); } @Override public void visitEnd() { logVoid(out, this, "visitEnd"); } @Override public void visitFieldInsn(int opcode, String owner, String name, String desc) { logVoid(out, this, "visitFieldInsn", opcode, owner, name, desc); } @Override public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) { logVoid(out, this, "visitFrame", type, nLocal, local, nStack, stack); } @Override public void visitIincInsn(int var, int increment) { logVoid(out, this, "visitIincInsn", var, increment); } @Override public void visitInsn(int opcode) { logVoid(out, this, "visitInsn", opcode); } @Override public AnnotationVisitor visitInsnAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) { return log(out, this, "visitInsnAnnotation", new TranslateAnnotationVisitor(out), typeRef, typePath, desc, visible); } @Override public void visitIntInsn(int opcode, int operand) { logVoid(out, this, "visitIntInsn", opcode, operand); } @Override public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) { logVoid(out, this, "visitInvokeDynamicInsn", name, desc, bsm, bsmArgs); } @Override public void visitJumpInsn(int opcode, Label label) { logVoid(out, this, "visitJumpInsn", opcode, label); } @Override public void visitLabel(Label label) { logVoid(out, this, "visitLabel", label); } @Override public void visitLdcInsn(Object cst) { logVoid(out, this, "visitLdcInsn", cst); } @Override public void visitLineNumber(int line, Label start) { if(!TRIM) logVoid(out, this, "visitLineNumber", line, start); } @Override public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) { if(!TRIM) logVoid(out, this, "visitLocalVariable", name, desc, signature, start, end, index); } @Override public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, TypePath typePath, Label[] start, Label[] end, int[] index, String desc, boolean visible) { return log(out, this, "visitLocalVariableAnnotation", new TranslateAnnotationVisitor(out), typePath, start, end, index, desc, visible); } @Override public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { logVoid(out, this, "visitLookupSwitchInsn", dflt, keys, labels); } @Override public void visitMaxs(int maxStack, int maxLocals) { logVoid(out, this, "visitMaxs", maxStack, maxLocals); } @Override public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { logVoid(out, this, "visitMethodInsn", opcode, owner, name, desc, itf); } @Override public void visitMultiANewArrayInsn(String desc, int dims) { logVoid(out, this, "visitMultiANewArrayInsn", desc, dims); } @Override public void visitParameter(String name, int access) { logVoid(out, this, "visitParameter", name, access); } @Override public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) { return log(out, this, "visitParameterAnnotation", new TranslateAnnotationVisitor(out), parameter, desc, visible); } @Override public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) { logVoid(out, this, "visitTableSwitchInsn", min, max, dflt, labels); } @Override public AnnotationVisitor visitTryCatchAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) { return log(out, this, "visitTryCatchAnnotation", new TranslateAnnotationVisitor(out), typeRef, typePath, desc, visible); } @Override public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { logVoid(out, this, "visitTryCatchBlock", start, end, handler, type); } @Override public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) { return log(out, this, "visitTypeAnnotation", new TranslateAnnotationVisitor(out), typeRef, typePath, desc, visible); } @Override public void visitTypeInsn(int opcode, String type) { logVoid(out, this, "visitTypeInsn", opcode, type); } @Override public void visitVarInsn(int opcode, int var) { logVoid(out, this, "visitVarInsn", opcode, var); } } private static class TranslateClassVisitor extends ClassVisitor { private PrintStream out; public TranslateClassVisitor(PrintStream out) { super(Opcodes.ASM5); this.out = out; } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { logVoid(out, this, "visit", version, access, name, signature, superName, interfaces); } @Override public AnnotationVisitor visitAnnotation(String desc, boolean visible) { return log(out, this, "visitAnnotation", new TranslateAnnotationVisitor(out), desc, visible); } @Override public void visitEnd() { logVoid(out, this, "visitEnd"); } @Override public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { return log(out, this, "visitField", new TranslateFieldVisitor(out), access, name, desc, signature, value); } @Override public void visitInnerClass(String name, String outerName, String innerName, int access) { logVoid(out, this, "visitInnerClass", name, outerName, innerName, access); } @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { resetLabelIDs(); return log(out, this, "visitMethod", new TranslateMethodVisitor(out), access, name, desc, signature, exceptions); } @Override public void visitOuterClass(String owner, String name, String desc) { logVoid(out, this, "visitOuterClass", owner, name, desc); } @Override public void visitSource(String source, String debug) { if(!TRIM) logVoid(out, this, "visitSource", source, debug); } @Override public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) { return log(out, this, "visitTypeAnnotation", new TranslateAnnotationVisitor(out), typeRef, typePath, desc, visible); } } }