view src/main/java/org/icedrobot/daneel/tools/DexDumper.java @ 129:f07b2934d275

Code drop of DEX dumper tool. * tools/DexDumper.java: Added new tooling class.
author Michael Starzinger <michi@complang.tuwien.ac.at>
date Tue, 12 Apr 2011 23:47:46 +0200
parents
children
line wrap: on
line source

/*
 * Daneel - Dalvik to Java bytecode compiler
 * Copyright (C) 2011  IcedRobot team
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * This file is subject to the "Classpath" exception:
 *
 * Linking this library statically or dynamically with other modules is
 * making a combined work based on this library.  Thus, the terms and
 * conditions of the GNU General Public License cover the whole
 * combination.
 *
 * As a special exception, the copyright holders of this library give you
 * permission to link this library with independent modules to produce an
 * executable, regardless of the license terms of these independent
 * modules, and to copy and distribute the resulting executable under terms
 * of your choice, provided that you also meet, for each linked independent
 * module, the terms and conditions of the license of that module.  An
 * independent module is a module which is not derived from or based on
 * this library.  If you modify this library, you may extend this exception
 * to your version of the library, but you are not obligated to do so.  If
 * you do not wish to do so, delete this exception statement from your
 * version.
 */

package org.icedrobot.daneel.tools;

import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.ByteBuffer;
import java.util.Arrays;

import org.icedrobot.daneel.dex.DexAnnotationVisitor;
import org.icedrobot.daneel.dex.DexClassVisitor;
import org.icedrobot.daneel.dex.DexFieldVisitor;
import org.icedrobot.daneel.dex.DexFile;
import org.icedrobot.daneel.dex.DexFileVisitor;
import org.icedrobot.daneel.dex.DexMethodVisitor;
import org.icedrobot.daneel.dex.Label;
import org.icedrobot.daneel.dex.Opcode;
import org.icedrobot.daneel.loader.ApkFile;
import org.objectweb.asm.Type;

/**
 * Simple tool which dumps the content of a DEX file. Mainly used to test the
 * parser functionality by hand.
 */
public class DexDumper {

    /**
     * Dumps all information contained in the given file (either a DEX or an APK
     * file) to the given output stream in human-readable format.
     * 
     * @param fileName The name of the file to be dumped.
     * @param out The output stream to print to.
     * @throws IOException In case of an error while accessing the file.
     */
    public void execute(String fileName, PrintStream out) throws IOException {
        DexFile dex;
        long m1 = System.currentTimeMillis();
        if (fileName.endsWith(".apk")) {
            ApkFile apk = new ApkFile(fileName);
            dex = apk.getDexFile();
        } else {
            dex = DexFile.parse(new File(fileName));
        }
        long m2 = System.currentTimeMillis();
        System.err.printf("Construction took %4dms.\n", m2 - m1);
        dex.accept(new FileDumper(out), 0);
        long m3 = System.currentTimeMillis();
        System.err.printf("Dumping took      %4dms.\n", m3 - m1);
    }

    /**
     * The dumper class taking care of printing file information.
     */
    private static class FileDumper implements DexFileVisitor {
        private final PrintStream out;

        public FileDumper(PrintStream out) {
            this.out = out;
        }

        @Override
        public DexClassVisitor visitClass(String name) {
            out.printf("class \"%s\":\n", convertDescToHuman(name));
            return new ClassDumper(out);
        }

        @Override
        public void visitEnd() {
            // Nothing to do here.
        }
    };

    /**
     * The dumper class taking care of printing class information.
     */
    private static class ClassDumper implements DexClassVisitor {
        private final PrintStream out;

        public ClassDumper(PrintStream out) {
            this.out = out;
        }

        @Override
        public void visit(int access, String name, String supername,
                String[] interfaces) {
            out.printf("  access: 0x%08x\n", access);
            out.printf("  name: %s\n", name);
            out.printf("  super: %s\n", supername);
            out.printf("  interfaces: %s\n", Arrays.toString(interfaces));
        }

        @Override
        public void visitEnd() {
            out.printf("end of class.\n\n");
        }

        @Override
        public DexAnnotationVisitor visitAnnotation(int visibility, String type) {
            out.printf("  annotation-on-class:\n");
            out.printf("    visibility: 0x%02x\n", visibility);
            out.printf("    type: %s\n", type);
            return new AnnotationDumper(out, "  ");
        }

        @Override
        public DexFieldVisitor visitField(int access, String name, String type,
                Object value) {
            out.printf("  field \"%s\":\n", name);
            out.printf("    access: 0x%08x\n", access);
            out.printf("    type: %s\n", type);
            out.printf("    value: %s\n", value);
            return new FieldDumper(out);
        }

        @Override
        public DexMethodVisitor visitMethod(int access, String name,
                String shorty, String returnType, String[] parameterTypes) {
            out.printf("  method \"%s\":\n", name);
            out.printf("    access: 0x%08x\n", access);
            out.printf("    shorty: %s\n", shorty);
            out.printf("    return-type: %s\n", returnType);
            out.printf("    parameter-types: %s\n", Arrays
                    .toString(parameterTypes));
            return new MethodDumper(out);
        }

        @Override
        public void visitSource(String source) {
            out.printf("  source: %s\n", source);
        }
    };

    /**
     * The dumper class taking care of printing field information.
     */
    private static class FieldDumper implements DexFieldVisitor {
        private final PrintStream out;

        public FieldDumper(PrintStream out) {
            this.out = out;
        }

        @Override
        public DexAnnotationVisitor visitAnnotation(int visibility, String type) {
            out.printf("    annotation-on-field:\n");
            out.printf("      visibility: 0x%02x\n", visibility);
            out.printf("      type: %s\n", type);
            return new AnnotationDumper(out, "    ");
        }

        @Override
        public void visitEnd() {
            // Nothing to do here.
        }
    };

    /**
     * The dumper class taking care of printing method information.
     */
    private static class MethodDumper implements DexMethodVisitor {
        private final PrintStream out;

        public MethodDumper(PrintStream out) {
            this.out = out;
        }

        @Override
        public DexAnnotationVisitor visitAnnotation(int visibility, String type) {
            out.printf("    annotation-on-method:\n");
            out.printf("      visibility: 0x%02x\n", visibility);
            out.printf("      type: %s\n", type);
            return new AnnotationDumper(out, "    ");
        }

        @Override
        public DexAnnotationVisitor visitParameterAnnotation(int parameter,
                int visibility, String type) {
            throw new RuntimeException("Implement me!");
            // return new DumpDexAnnotationVisitor()
        }

        @Override
        public void visitCode(int registers, int ins, int outs) {
            out.printf("    code (r=%d, i=%d, o=%d):\n", registers, ins, outs);
        }

        @Override
        public void visitEnd() {
            out.printf("  end of method.\n");
        }

        @Override
        public void visitInstr(Opcode opcode) {
            out.printf("      %s\n", opcode.name());
        }

        @Override
        public void visitInstrArray(Opcode opcode, int vsrcOrDest, int varray,
                int vindex) {
            out.printf("      %s v%d v%d v%d\n", opcode.name(), vsrcOrDest,
                    varray, vindex);
        }

        @Override
        public void visitInstrBinOp(Opcode opcode, int vdest, int vsrc,
                int vsrc2) {
            out.printf("      %s v%d v%d v%d\n", opcode.name(), vdest, vsrc,
                    vsrc2);
        }

        @Override
        public void visitInstrBinOpAndLiteral(Opcode opcode, int vdest,
                int vsrc, int value) {
            out.printf("      %s v%d v%d #%d(0x%08x)\n", opcode.name(), vdest,
                    vsrc, value, value);
        }

        @Override
        public void visitInstrClass(Opcode opcode, int vdest, String type) {
            String classRef = convertDescToHuman(type);
            out.printf("      %s v%d [%s]\n", opcode.name(), vdest, classRef);
        }

        @Override
        public void visitInstrConstString(Opcode opcode, int vdest, String value) {
            out.printf("      %s v%d \"%s\"\n", opcode.name(), vdest, value);
        }

        @Override
        public void visitInstrConstU32(Opcode opcode, int vdest, int value) {
            out.printf("      %s v%d #%d(0x%08x)\n", opcode.name(), vdest,
                    value, value);
        }

        @Override
        public void visitInstrConstU64(Opcode opcode, int vdest, long value) {
            out.printf("      %s v%d #%d(0x%016x)\n", opcode.name(), vdest,
                    value, value);
        }

        @Override
        public void visitInstrField(Opcode opcode, int vsrcOrDest, int vref,
                String owner, String name, String desc) {
            String fieldRef = convertDescToHuman(desc) + ' '
                    + convertDescToHuman(owner) + '#' + name;
            out.printf("      %s v%d v%d [%s]\n", opcode.name(), vsrcOrDest,
                    vref, fieldRef);
        }

        @Override
        public void visitInstrFillArrayData(Opcode opcode, int vsrc,
                int elementWidth, int elementNumber, ByteBuffer data) {
            out.printf("      %s v%d %s\n", opcode.name(), vsrc, data);
        }

        @Override
        public void visitInstrFilledNewArray(Opcode opcode, int num, int va,
                int vpacked, String type) {
            out.printf("      %s v%d+%d, %s\n", opcode.name(), va, num, type);
        }

        @Override
        public void visitInstrGoto(Opcode opcode, Label label) {
            out.printf("      %s %s\n", opcode.name(), label);
        }

        @Override
        public void visitInstrIfTest(Opcode opcode, int vsrc1, int vsrc2,
                Label label) {
            out.printf("      %s v%d v%d %s\n", opcode.name(), vsrc1, vsrc2,
                    label);
        }

        @Override
        public void visitInstrIfTestZ(Opcode opcode, int vsrc, Label label) {
            out.printf("      %s v%d %s\n", opcode.name(), vsrc, label);
        }

        @Override
        public void visitInstrInstanceof(Opcode opcode, int vdest, int vsrc,
                String type) {
            out.printf("      %s v%d v%d %s\n", opcode.name(), vdest, vsrc,
                    type);
        }

        @Override
        public void visitInstrMethod(Opcode opcode, int num, int va,
                int vpacked, String owner, String name, String desc) {
            String methodRef = convertDescToHuman(owner) + "#" + name + desc;
            out.printf("      %s {%x%04x}+%d [%s]\n", opcode.name(), va,
                    vpacked, num, methodRef);
        }

        @Override
        public void visitInstrNewArray(Opcode opcode, int vdest, int vsize,
                String type) {
            out.printf("      %s v%d v%d %s\n", opcode.name(), vdest, vsize,
                    type);
        }

        @Override
        public void visitInstrOp(Opcode opcode, int srcOrDst) {
            out.printf("      %s v%d\n", opcode.name(), srcOrDst);
        }

        @Override
        public void visitInstrPackedSwitch(Opcode opcode, int vsrc,
                int firstKey, Label[] targets) {
            out.printf("      %s v%d %s\n", opcode.name(), vsrc, Arrays
                    .toString(targets));
        }

        @Override
        public void visitInstrSparseSwitch(Opcode opcode, int vsrc, int[] keys,
                Label[] targets) {
            out.printf("      %s v%d %s\n", opcode.name(), vsrc, Arrays
                    .toString(targets));
        }

        @Override
        public void visitInstrUnaryOp(Opcode opcode, int vdest, int vsrc) {
            out.printf("      %s v%d v%d\n", opcode.name(), vdest, vsrc);
        }

        @Override
        public void visitLabel(Label label) {
            out.printf("      --- %s:\n", label);
        }

        @Override
        public void visitTryCatch(Label start, Label end, Label handler,
                String type) {
            out.printf("    try-catch: %s-%s -> %s, %s\n", start, end, handler,
                    type);
        }

        @Override
        public void visitLineNumber(String source, int line, Label start) {
            out.printf("    line-number: %s -> %s:%d\n", start, source, line);
        }

        @Override
        public void visitLocalVariable(String name, String desc, Label start,
                Label end, int reg) {
            String varRef = convertDescToHuman(desc) + ' ' + name;
            out.printf("    local-var: %s-%s, v%d -> [%s]\n", start, end, reg,
                    varRef);
        }
    };

    /**
     * The dumper class taking care of printing annotation information.
     */
    private static class AnnotationDumper implements DexAnnotationVisitor {
        private final PrintStream out;
        private final String indent;

        public AnnotationDumper(PrintStream out, String indent) {
            this.out = out;
            this.indent = indent;
        }

        @Override
        public void visitPrimitive(String name, Object value) {
            out.printf("%s  primitive-value: %s = %s\n", indent, name, value);
        }

        @Override
        public DexAnnotationVisitor visitAnnotation(String name, String type) {
            throw new UnsupportedOperationException();
        }

        @Override
        public DexAnnotationVisitor visitArray(String name, int size) {
            out.printf("%s  array-value: %s = array[%d]\n", indent, name, size);
            return null;
        }

        @Override
        public void visitEnum(String name, String enumOwner, String enumName) {
            out.printf("%s  enum-value: %s = %s %s\n", indent, name, enumOwner,
                    enumName);
        }

        @Override
        public void visitField(String name, String fieldOwner,
                String fieldName, String fieldDesc) {
            out.printf("%s  field-value: %s = %s %s %s\n", indent, name,
                    fieldOwner, fieldName, fieldDesc);
        }

        @Override
        public void visitMethod(String name, String methodOwner,
                String methodName, String methodDesc) {
            out.printf("%s  method-value: %s = %s %s %s\n", indent, name,
                    methodOwner, methodName, methodDesc);
        }

        @Override
        public void visitType(String name, String typeDesc) {
            out.printf("%s  type-value: %s = %s\n", indent, name, typeDesc);
        }

        @Override
        public void visitEnd() {
            out.printf("%send of annotation.\n", indent);
        }
    };

    /**
     * Converts a type descriptor into a human-readable representation.
     * 
     * @param desc The given type descriptor.
     * @return The human-readable representation.
     */
    static String convertDescToHuman(String desc) {
        return Type.getType(desc).getClassName();
    }

    public static void main(String[] args) throws Exception {
        DexDumper dumper = new DexDumper();
        dumper.execute(args[0], System.out);
    }
}