changeset 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 68531618d334
children 96c63feb973c
files src/main/java/org/icedrobot/daneel/dex/ClassData.java src/main/java/org/icedrobot/daneel/dex/Code.java src/main/java/org/icedrobot/daneel/dex/DebugInfo.java src/main/java/org/icedrobot/daneel/util/TypeUtil.java
diffstat 4 files changed, 98 insertions(+), 48 deletions(-) [+]
line wrap: on
line diff
--- a/src/main/java/org/icedrobot/daneel/dex/ClassData.java	Fri Mar 25 16:08:30 2011 +0100
+++ b/src/main/java/org/icedrobot/daneel/dex/ClassData.java	Fri Mar 25 23:59:09 2011 +0100
@@ -123,12 +123,13 @@
         int directMethodsIdx = 0;
         for (int i = 0; i < directMethodsSize; i++) {
             directMethodsIdx += BufferUtil.getULEB128(buffer);
-            directMethodsIds[i] = dex.getMethodId(directMethodsIdx);
+            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);
+                        dex, method);
         }
 
         // Parse encoded_method structures in virtual_methods array.
@@ -138,12 +139,13 @@
         int virtualMethodsIdx = 0;
         for (int i = 0; i < virtualMethodsSize; i++) {
             virtualMethodsIdx += BufferUtil.getULEB128(buffer);
-            virtualMethodsIds[i] = dex.getMethodId(virtualMethodsIdx);
+            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);
+                        dex, method);
         }
 
         // Parse encoded_array_item and contained encoded_value structures.
--- a/src/main/java/org/icedrobot/daneel/dex/Code.java	Fri Mar 25 16:08:30 2011 +0100
+++ b/src/main/java/org/icedrobot/daneel/dex/Code.java	Fri Mar 25 23:59:09 2011 +0100
@@ -61,14 +61,17 @@
      * 
      * @param buffer The byte buffer to read from.
      * @param dex The DEX file currently being parsed.
+     * @param method The method identifier this code belongs to.
      * @return An object representing the parsed data.
      */
-    public static Code parse(ByteBuffer buffer, DexFile dex) {
-        return new Code(buffer, dex);
+    public static Code parse(ByteBuffer buffer, DexFile dex, MethodId method) {
+        return new Code(buffer, dex, method);
     }
 
     private final DexFile dex;
 
+    private final MethodId method;
+
     private final int registersSize;
 
     private final int insSize;
@@ -85,8 +88,9 @@
 
     private final List<TryCatchInfo> tryCatchInfos;
 
-    private Code(ByteBuffer buffer, DexFile dex) {
+    private Code(ByteBuffer buffer, DexFile dex, MethodId method) {
         this.dex = dex;
+        this.method = method;
         registersSize = buffer.getShort();
         insSize = buffer.getShort();
         outsSize = buffer.getShort();
@@ -152,6 +156,15 @@
     }
 
     /**
+     * Returns the method identifier of the method this code belongs to.
+     * 
+     * @return The method identifier object.
+     */
+    public MethodId getMethod() {
+        return method;
+    }
+
+    /**
      * Returns the number of registers used by the code as specified in the DEX
      * file.
      * 
@@ -168,25 +181,21 @@
      */
     public void accept(DexMethodVisitor visitor) {
         visitor.visitCode(registersSize, insSize, outsSize);
-        
+
         // Visit try-catch block information if available.
         for (TryCatchInfo tryCatch : tryCatchInfos)
             visitor.visitTryCatch(tryCatch.startLabel, tryCatch.endLabel,
                     tryCatch.handlerLabel, tryCatch.type);
-        
-        // visit instructions
+
+        // Visit instructions.
         acceptInsns(visitor);
 
         // Visit local variable information if available.
         if (debugInfo != null)
-            for (LocalVariable local : debugInfo.getLocalVariables()) {
-                String name = (local.nameIdx != DexFile.NO_INDEX) ? dex
-                        .getString(local.nameIdx) : null;
-                String desc = (local.typeIdx != DexFile.NO_INDEX) ? dex
-                        .getTypeDescriptor(local.typeIdx) : null;
-                visitor.visitLocalVariable(name, desc, local.startLabel,
-                        local.endLabel, local.regNum);
-            }
+            for (LocalVariable local : debugInfo.getLocalVariables())
+                visitor.visitLocalVariable(local.name, local.type,
+                        local.startLabel, local.endLabel, local.regNum);
+
     }
 
     /**
--- a/src/main/java/org/icedrobot/daneel/dex/DebugInfo.java	Fri Mar 25 16:08:30 2011 +0100
+++ b/src/main/java/org/icedrobot/daneel/dex/DebugInfo.java	Fri Mar 25 23:59:09 2011 +0100
@@ -43,6 +43,7 @@
 
 import org.icedrobot.daneel.dex.Code.DebugLabel;
 import org.icedrobot.daneel.util.BufferUtil;
+import org.icedrobot.daneel.util.TypeUtil;
 
 /**
  * A parser class capable of parsing {@code debug_info_item} structures as part
@@ -75,6 +76,8 @@
         return new DebugInfo(buffer, dex, code);
     }
 
+    private final DexFile dex;
+
     private final Code code;
 
     private final int lineStart;
@@ -86,6 +89,7 @@
     private final List<LocalVariable> localVariables;
 
     private DebugInfo(ByteBuffer buffer, DexFile dex, Code code) {
+        this.dex = dex;
         this.code = code;
         lineStart = BufferUtil.getULEB128(buffer);
         parametersSize = BufferUtil.getULEB128(buffer);
@@ -146,10 +150,26 @@
         int maxRegs = code.getRegistersSize();
         LocalVariable[] regs = new LocalVariable[maxRegs];
 
+        // Emit local variables for method parameters.
+        String[] parameterTypes = code.getMethod().getProtoId().getParameters();
+        if (parameterTypes != null) {
+            if (parametersSize != parameterTypes.length)
+                throw new DexParseException("Parameter count does not match.");
+            int regNum = maxRegs - 1;
+            for (int i = parametersSize - 1; i >= 0; i--, regNum--) {
+                String name = parameterNames[i];
+                String type = parameterTypes[i];
+                if (TypeUtil.isWideType(type))
+                    regNum--;
+                regs[regNum] = emitLocalVariable(regNum, 0, name, type, null);
+            }
+        }
+
         // Iterate over all state machine bytecodes.
         while (buffer.hasRemaining()) {
             int opcode = buffer.get() & 0xff;
             int regNum, nameIdx, typeIdx, sigIdx;
+            String name, type, sig;
             LocalVariable local;
 
             // Switch over all possible bytecodes.
@@ -169,11 +189,12 @@
                 regNum = BufferUtil.getULEB128(buffer);
                 nameIdx = BufferUtil.getULEB128(buffer) - 1;
                 typeIdx = BufferUtil.getULEB128(buffer) - 1;
+                name = resolveString(nameIdx);
+                type = resolveType(typeIdx);
                 local = regs[regNum];
                 if (local != null && local.endLabel == null)
                     throw new DexParseException("Live local in register.");
-                regs[regNum] = emitLocalVariable(regNum, addr, nameIdx,
-                        typeIdx, DexFile.NO_INDEX);
+                regs[regNum] = emitLocalVariable(regNum, addr, name, type, null);
                 break;
 
             case DBG_START_LOCAL_EXTENDED:
@@ -181,30 +202,34 @@
                 nameIdx = BufferUtil.getULEB128(buffer) - 1;
                 typeIdx = BufferUtil.getULEB128(buffer) - 1;
                 sigIdx = BufferUtil.getULEB128(buffer) - 1;
+                name = resolveString(nameIdx);
+                type = resolveType(typeIdx);
+                sig = resolveString(sigIdx);
                 local = regs[regNum];
                 if (local != null && local.endLabel == null)
                     throw new DexParseException("Live local in register.");
-                regs[regNum] = emitLocalVariable(regNum, addr, nameIdx,
-                        typeIdx, sigIdx);
+                regs[regNum] = emitLocalVariable(regNum, addr, name, type, sig);
                 break;
 
             case DBG_END_LOCAL:
                 regNum = BufferUtil.getULEB128(buffer);
                 local = regs[regNum];
                 if (local == null || local.endLabel != null)
-                    // XXX Check why this happens, this shouldn't happen.
-                    //throw new DexParseException("No live local in register.");
-                    break;
-                local.setEnd(code.putLabel(addr, false));
+                    throw new DexParseException("No live local in register.");
+                local.endLabel = code.putLabel(addr, false);
                 break;
 
             case DBG_RESTART_LOCAL:
                 regNum = BufferUtil.getULEB128(buffer);
                 local = regs[regNum];
                 if (local == null || local.endLabel == null)
-                    throw new DexParseException("No local to re-introduce.");
-                regs[regNum] = emitLocalVariable(regNum, addr, local.nameIdx,
-                        local.typeIdx, local.sigIdx);
+                    // XXX Check why this happens, this shouldn't happen.
+                    //throw new DexParseException("No local to re-introduce.");
+                    break;
+                name = local.name;
+                type = local.type;
+                sig = local.sig;
+                regs[regNum] = emitLocalVariable(regNum, addr, name, type, sig);
                 break;
 
             case DBG_SET_PROLOGUE_END:
@@ -230,6 +255,14 @@
         throw new DexParseException("Premature end of state machine.");
     }
 
+    private String resolveString(int idx) {
+        return (idx != DexFile.NO_INDEX) ? dex.getString(idx) : null;
+    }
+
+    private String resolveType(int idx) {
+        return (idx != DexFile.NO_INDEX) ? dex.getTypeDescriptor(idx) : null;
+    }
+
     /**
      * Emits a new local variable information. The returned object has all
      * information set correctly, except for the label at which the local
@@ -237,16 +270,16 @@
      * 
      * @param regNum The register number that will contain the local variable.
      * @param startAddr The instruction offset where the local variable starts.
-     * @param nameIdx The index of the variable name or {@code NO_INDEX}.
-     * @param typeIdx The index of the type descriptor or {@code NO_INDEX}.
-     * @param sigIdx The index of the type signature or {@code NO_INDEX}.
+     * @param name The local variable's name or {@code null}.
+     * @param type The local variable's type descriptor or {@code null}.
+     * @param sig The local variable's type signature or {@code null}.
      * @return The object representing the local variable information.
      */
     private LocalVariable emitLocalVariable(int regNum, int startAddr,
-            int nameIdx, int typeIdx, int sigIdx) {
+            String name, String type, String sig) {
         Label startLabel = code.putLabel(startAddr, false);
-        LocalVariable local = new LocalVariable(regNum, nameIdx, typeIdx,
-                sigIdx, startLabel);
+        LocalVariable local = new LocalVariable(regNum, name, type, sig,
+                startLabel);
         localVariables.add(local);
         return local;
     }
@@ -276,26 +309,20 @@
      */
     static class LocalVariable {
         final int regNum;
-        final int nameIdx;
-        final int typeIdx;
-        final int sigIdx;
+        final String name;
+        final String type;
+        final String sig;
         final Label startLabel;
         Label endLabel;
 
-        public LocalVariable(int regNum, int nameIdx, int typeIdx, int sigIdx,
+        public LocalVariable(int regNum, String name, String type, String sig,
                 Label startLabel) {
             this.regNum = regNum;
-            this.nameIdx = nameIdx;
-            this.typeIdx = typeIdx;
-            this.sigIdx = sigIdx;
+            this.name = name;
+            this.type = type;
+            this.sig = sig;
             this.startLabel = startLabel;
         }
-
-        public void setEnd(Label endLabel) {
-            if (this.endLabel != null)
-                throw new DexParseException("Local variable is dead.");
-            this.endLabel = endLabel;
-        }
     };
 
     /**
--- a/src/main/java/org/icedrobot/daneel/util/TypeUtil.java	Fri Mar 25 16:08:30 2011 +0100
+++ b/src/main/java/org/icedrobot/daneel/util/TypeUtil.java	Fri Mar 25 23:59:09 2011 +0100
@@ -107,4 +107,16 @@
         }
         return internalNames;
     }
+
+    /**
+     * Checks whether the given type descriptor is a wide type. That means that
+     * it requires two machine words (e.g. register pair, two local variable
+     * slots) to store such a value.
+     * 
+     * @param desc The given type descriptor.
+     * @return True if the type is wide, false otherwise.
+     */
+    public static boolean isWideType(String desc) {
+        return desc.equals("J") || desc.equals("D");
+    }
 }