view src/main/java/org/icedrobot/daneel/dex/ClassData.java @ 80:817b7941153f

Fixed debug information for method parameters. * dex/ClassData.java: Pass method identifier to code parser. * dex/Code.java: Take method identifier as argument. * dex/DebugInfo.java (interpret): Also emit locals for method parameters. * util/TypeUtil.java (isWideType): New utility method.
author Michael Starzinger <michi@complang.tuwien.ac.at>
date Fri, 25 Mar 2011 23:59:09 +0100
parents ef7e8137da03
children ff944d4bdfb7
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.dex;

import java.nio.ByteBuffer;

import org.icedrobot.daneel.util.BufferUtil;

/**
 * A parser class capable of parsing {@code class_data_item} structures as part
 * of DEX files. Keep package-private to hide internal API.
 */
class ClassData {

    /**
     * Parses a {@code class_data_item} structure in a DEX file at the buffer's
     * current position.
     * 
     * @param buffer The byte buffer to read from.
     * @param dex The DEX file currently being parsed.
     * @param classDef The class definition this data belongs to.
     * @return An object representing the parsed data.
     */
    public static ClassData parse(ByteBuffer buffer, DexFile dex,
            ClassDef classDef) {
        return new ClassData(buffer, dex, classDef);
    }

    private final ClassDef classDef;

    private int staticFieldsSize;

    private int instanceFieldsSize;

    private int directMethodsSize;

    private int virtualMethodsSize;

    private FieldId[] staticFieldsIds;
    private int[] staticFieldsFlags;

    private FieldId[] instanceFieldsIds;
    private int[] instanceFieldsFlags;

    private final MethodId[] directMethodsIds;
    private int[] directMethodsFlags;
    private final Code[] directMethodsCode;

    private final MethodId[] virtualMethodsIds;
    private int[] virtualMethodsFlags;
    private final Code[] virtualMethodsCode;

    private final Object[] staticValues;

    private ClassData(ByteBuffer buffer, DexFile dex, ClassDef classDef) {
        this.classDef = classDef;
        staticFieldsSize = BufferUtil.getULEB128(buffer);
        instanceFieldsSize = BufferUtil.getULEB128(buffer);
        directMethodsSize = BufferUtil.getULEB128(buffer);
        virtualMethodsSize = BufferUtil.getULEB128(buffer);

        // Parse encoded_field structures in static_fields array and resolve
        // field id indices.
        staticFieldsIds = new FieldId[staticFieldsSize];
        staticFieldsFlags = new int[staticFieldsSize];
        int staticFieldsIdx = 0;
        for (int i = 0; i < staticFieldsSize; i++) {
            staticFieldsIdx += BufferUtil.getULEB128(buffer);
            staticFieldsIds[i] = dex.getFieldId(staticFieldsIdx);
            staticFieldsFlags[i] = BufferUtil.getULEB128(buffer);
        }

        // Parse encoded_field structures in instance_fields array and resolve
        // field id indices.
        instanceFieldsIds = new FieldId[instanceFieldsSize];
        instanceFieldsFlags = new int[instanceFieldsSize];
        int instanceFieldsIdx = 0;
        for (int i = 0; i < instanceFieldsSize; i++) {
            instanceFieldsIdx += BufferUtil.getULEB128(buffer);
            instanceFieldsIds[i] = dex.getFieldId(instanceFieldsIdx);
            instanceFieldsFlags[i] = BufferUtil.getULEB128(buffer);
        }

        // Parse encoded_method structures in direct_methods array.
        directMethodsIds = new MethodId[directMethodsSize];
        directMethodsFlags = new int[directMethodsSize];
        directMethodsCode = new Code[directMethodsSize];
        int directMethodsIdx = 0;
        for (int i = 0; i < directMethodsSize; i++) {
            directMethodsIdx += BufferUtil.getULEB128(buffer);
            MethodId method = dex.getMethodId(directMethodsIdx);
            directMethodsIds[i] = method;
            directMethodsFlags[i] = BufferUtil.getULEB128(buffer);
            int codeOff = BufferUtil.getULEB128(buffer);
            if (codeOff != 0)
                directMethodsCode[i] = Code.parse(dex.getDataBuffer(codeOff),
                        dex, method);
        }

        // Parse encoded_method structures in virtual_methods array.
        virtualMethodsIds = new MethodId[virtualMethodsSize];
        virtualMethodsFlags = new int[virtualMethodsSize];
        virtualMethodsCode = new Code[virtualMethodsSize];
        int virtualMethodsIdx = 0;
        for (int i = 0; i < virtualMethodsSize; i++) {
            virtualMethodsIdx += BufferUtil.getULEB128(buffer);
            MethodId method = dex.getMethodId(virtualMethodsIdx);
            virtualMethodsIds[i] = method;
            virtualMethodsFlags[i] = BufferUtil.getULEB128(buffer);
            int codeOff = BufferUtil.getULEB128(buffer);
            if (codeOff != 0)
                virtualMethodsCode[i] = Code.parse(dex.getDataBuffer(codeOff),
                        dex, method);
        }

        // Parse encoded_array_item and contained encoded_value structures.
        int staticValuesOff = classDef.getStaticValuesOff();
        if (staticValuesOff != 0) {
            ByteBuffer buf = dex.getDataBuffer(staticValuesOff);
            staticValues = EncodedValue.parseArray(buf, dex);
        } else
            staticValues = new Object[0];
    }

    public int getStaticFieldsSize() {
        return staticFieldsSize;
    }

    public int getInstanceFieldsSize() {
        return instanceFieldsSize;
    }

    public int getDirectMethodsSize() {
        return directMethodsSize;
    }

    public int getVirtualMethodsSize() {
        return virtualMethodsSize;
    }

    public FieldId getStaticFieldsId(int idx) {
        return staticFieldsIds[idx];
    }

    public int getStaticFieldsFlag(int idx) {
        return staticFieldsFlags[idx];
    }

    public FieldId getInstanceFieldsId(int idx) {
        return instanceFieldsIds[idx];
    }

    public int getInstanceFieldsFlag(int idx) {
        return instanceFieldsFlags[idx];
    }

    public int getDirectMethodsFlag(int idx) {
        return directMethodsFlags[idx];
    }

    public int getVirtualMethodsFlag(int idx) {
        return virtualMethodsFlags[idx];
    }

    /**
     * Allows the given visitor to visit this class data object.
     * 
     * @param visitor The given DEX class visitor object.
     */
    public void accept(DexClassVisitor visitor) {

        // Visit all static fields.
        for (int i = 0; i < staticFieldsSize; i++) {
            int access = staticFieldsFlags[i];
            FieldId field = staticFieldsIds[i];
            Object value = (i < staticValues.length) ? staticValues[i] : null;
            acceptField(visitor, access, field, value);
        }

        // Visit all instance fields.
        for (int i = 0; i < instanceFieldsSize; i++) {
            int access = instanceFieldsFlags[i];
            FieldId field = instanceFieldsIds[i];
            acceptField(visitor, access, field, null);
        }

        // Visit all direct methods.
        for (int i = 0; i < directMethodsSize; i++) {
            int access = directMethodsFlags[i];
            MethodId method = directMethodsIds[i];
            Code code = directMethodsCode[i];
            acceptMethod(visitor, access, method, code);
        }

        // Visit all virtual methods.
        for (int i = 0; i < virtualMethodsSize; i++) {
            int access = virtualMethodsFlags[i];
            MethodId method = virtualMethodsIds[i];
            Code code = virtualMethodsCode[i];
            acceptMethod(visitor, access, method, code);
        }
    }

    /**
     * Helper method to visit a field.
     */
    private void acceptField(DexClassVisitor visitor, int access,
            FieldId field, Object value) {
        DexFieldVisitor dfv = visitor.visitField(access, field.getName(), field
                .getTypeDescriptor(), value);
        if (dfv == null)
            return;
        AnnotationsDirectory annotations = classDef.getAnnotations();
        if (annotations != null)
            annotations.acceptFieldAnnotations(dfv, field);
        dfv.visitEnd();
    }

    /**
     * Helper method to visit a method.
     */
    private void acceptMethod(DexClassVisitor visitor, int access,
            MethodId method, Code code) {
        ProtoId proto = method.getProtoId();
        DexMethodVisitor dmv = visitor
                .visitMethod(access, method.getName(), proto.getShorty(),
                        proto.getReturnType(), proto.getParameters());
        if (dmv == null)
            return;
        AnnotationsDirectory annotations = classDef.getAnnotations();
        if (annotations != null)
            annotations.acceptMethodAnnotations(dmv, method);
        if (code != null)
            code.accept(dmv);
        dmv.visitEnd();
    }
}