changeset 91:2711ac37fdfb

Fixed local variable information for DEX files. * dex/Code.java: Added end label marking the instruction stream end. * dex/DebugInfo.java (interpret): Optimistic approach for variable closing. * rewriter/DexRewriter.java (visitLocalVariable): Implemented. * rewriter/PatchMethodVisitor.java: Pass local variables to actual visitor. * rewriter/GenericTest.java: Enabled test case.
author Michael Starzinger <michi@complang.tuwien.ac.at>
date Mon, 28 Mar 2011 00:29:55 +0200
parents b7b15d19d6ef
children eafd801c8fa0
files src/main/java/org/icedrobot/daneel/dex/Code.java src/main/java/org/icedrobot/daneel/dex/DebugInfo.java src/main/java/org/icedrobot/daneel/rewriter/DexRewriter.java src/main/java/org/icedrobot/daneel/rewriter/PatchMethodVisitor.java src/test/java/org/icedrobot/daneel/rewriter/GenericTest.java
diffstat 5 files changed, 58 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/src/main/java/org/icedrobot/daneel/dex/Code.java	Mon Mar 28 00:03:04 2011 +0200
+++ b/src/main/java/org/icedrobot/daneel/dex/Code.java	Mon Mar 28 00:29:55 2011 +0200
@@ -715,6 +715,9 @@
                 throw new DexParseException("Unkown opcode: " + op);
             }
         }
+
+        // Visit the end label after all instructions.
+        v.visitLabel(getEndLabel());
     }
 
     /** The array associating bytecode instructions to their lengths. */
@@ -753,6 +756,20 @@
     /** The map associating bytecode positions with labels. */
     private final Map<Integer, Label> labelMap = new HashMap<Integer, Label>();
 
+    /** The end label used to mark the address after the last instruction. */
+    private final Label endLabel = new Label() {
+
+        @Override
+        public boolean isJumpTarget() {
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            return "LEnd";
+        }
+    };
+
     /**
      * Returns the label associated with the given bytecode position.
      * 
@@ -770,6 +787,16 @@
     }
 
     /**
+     * Returns the label marking the end of the instruction stream. The label
+     * actually points to the address after the last instruction.
+     * 
+     * @return The associated label, never returns {@code null}.
+     */
+    Label getEndLabel() {
+        return endLabel;
+    }
+
+    /**
      * Adds a new label to the given bytecode position. Keep this method
      * package-private in favor of private as long as there are inner classes
      * which call it, to avoid trampoline code.
--- a/src/main/java/org/icedrobot/daneel/dex/DebugInfo.java	Mon Mar 28 00:03:04 2011 +0200
+++ b/src/main/java/org/icedrobot/daneel/dex/DebugInfo.java	Mon Mar 28 00:29:55 2011 +0200
@@ -175,6 +175,9 @@
             // Switch over all possible bytecodes.
             switch (opcode) {
             case DBG_END_SEQUENCE:
+                for (LocalVariable open : regs)
+                    if (open != null && open.endLabel == null)
+                        open.endLabel = code.getEndLabel();
                 return;
 
             case DBG_ADVANCE_PC:
@@ -193,7 +196,7 @@
                 type = resolveType(typeIdx);
                 local = regs[regNum];
                 if (local != null && local.endLabel == null)
-                    throw new DexParseException("Live local in register.");
+                    closeLocalVariable(local, addr);
                 regs[regNum] = emitLocalVariable(regNum, addr, name, type, null);
                 break;
 
@@ -207,7 +210,7 @@
                 sig = resolveString(sigIdx);
                 local = regs[regNum];
                 if (local != null && local.endLabel == null)
-                    throw new DexParseException("Live local in register.");
+                    closeLocalVariable(local, addr);
                 regs[regNum] = emitLocalVariable(regNum, addr, name, type, sig);
                 break;
 
@@ -216,16 +219,16 @@
                 local = regs[regNum];
                 if (local == null || local.endLabel != null)
                     throw new DexParseException("No live local in register.");
-                local.endLabel = code.putLabel(addr, false);
+                closeLocalVariable(local, addr);
                 break;
 
             case DBG_RESTART_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 local to re-introduce.");
-                    break;
+                if (local == null)
+                    throw new DexParseException("No local to re-introduce.");
+                if (local != null && local.endLabel == null)
+                    closeLocalVariable(local, addr);
                 name = local.name;
                 type = local.type;
                 sig = local.sig;
@@ -285,6 +288,21 @@
     }
 
     /**
+     * Closes a previously emitted local variable information. The label at
+     * which the local variable runs out of scope is set here.
+     * 
+     * @param local The local variable information.
+     * @param endAddr The instruction offset where the local variable ends.
+     */
+    private void closeLocalVariable(LocalVariable local, int endAddr) {
+        Label endLabel = code.putLabel(endAddr, false);
+        if (endLabel == local.startLabel)
+            localVariables.remove(local);
+        else
+            local.endLabel = endLabel;
+    }
+
+    /**
      * Emits a new line number entry. Line numbers are stored as part of the
      * label they are associated with.
      * 
--- a/src/main/java/org/icedrobot/daneel/rewriter/DexRewriter.java	Mon Mar 28 00:03:04 2011 +0200
+++ b/src/main/java/org/icedrobot/daneel/rewriter/DexRewriter.java	Mon Mar 28 00:29:55 2011 +0200
@@ -322,7 +322,11 @@
         @Override
         public void visitLocalVariable(String name, String desc, Label start,
                 Label end, int reg) {
-            // XXX Ignore debug information for now.
+            reg = registerToSlot(reg);
+            if (name != null && desc != null) {
+                mv.visitLocalVariable(name, desc, null, getASMLabel(start),
+                        getASMLabel(end), reg);
+            }
         }
 
         @Override
--- a/src/main/java/org/icedrobot/daneel/rewriter/PatchMethodVisitor.java	Mon Mar 28 00:03:04 2011 +0200
+++ b/src/main/java/org/icedrobot/daneel/rewriter/PatchMethodVisitor.java	Mon Mar 28 00:29:55 2011 +0200
@@ -218,7 +218,7 @@
     @Override
     public void visitLocalVariable(String name, String desc, String signature,
             Label start, Label end, int index) {
-        mv().visitLocalVariable(name, desc, signature, start, end, index);
+        mv.visitLocalVariable(name, desc, signature, start, end, index);
     }
 
     @Override
--- a/src/test/java/org/icedrobot/daneel/rewriter/GenericTest.java	Mon Mar 28 00:03:04 2011 +0200
+++ b/src/test/java/org/icedrobot/daneel/rewriter/GenericTest.java	Mon Mar 28 00:29:55 2011 +0200
@@ -46,7 +46,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-@org.junit.Ignore("Generic Debug Info not work")
 @RunWith(DexifyingRunner.class)
 public class GenericTest {