Mercurial > hg > icedrobot > daneel
changeset 101:9813956b6840
Fixed line number table handling for DEX files.
* dex/Code.java: Simplified line number table handling.
* (findLabels): Removed obsolete hacks for line number labels.
* dex/DebugInfo.java (getLineNumbers): Returns a separate line number table.
* (interpret): Implemented handling of DBG_SET_FILE operation.
* (emitLineNumber): Switched to new table, thereby fixing duplication issue.
* (LineNumber): Internal representation of line number entries.
* rewriter/DexRewriter.java (visitLineNumber): Implemented.
author | Michael Starzinger <michi@complang.tuwien.ac.at> |
---|---|
date | Mon, 28 Mar 2011 22:59:38 +0200 |
parents | 8d6b719ff9a1 |
children | 8f8bd597644c |
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 |
diffstat | 3 files changed, 72 insertions(+), 81 deletions(-) [+] |
line wrap: on
line diff
--- a/src/main/java/org/icedrobot/daneel/dex/Code.java Mon Mar 28 22:11:23 2011 +0200 +++ b/src/main/java/org/icedrobot/daneel/dex/Code.java Mon Mar 28 22:59:38 2011 +0200 @@ -44,7 +44,7 @@ import java.util.List; import java.util.Map; -import org.icedrobot.daneel.dex.DebugInfo.LineNumberLabel; +import org.icedrobot.daneel.dex.DebugInfo.LineNumber; import org.icedrobot.daneel.dex.DebugInfo.LocalVariable; import org.icedrobot.daneel.util.BufferUtil; import org.icedrobot.daneel.util.TypeUtil; @@ -101,14 +101,6 @@ // Keep a separate buffer for the instructions array. insns = (ShortBuffer) buffer.asShortBuffer().limit(insnsSize); - // Parse any associated debug info. Remember to do this before finding - // labels of branch targets, because it generates line number labels. - if (debugInfoOff != 0) { - ByteBuffer buf = dex.getDataBuffer(debugInfoOff); - debugInfo = DebugInfo.parse(buf, dex, this); - } else - debugInfo = null; - // Find labels inside the instruction stream and also parse possible // in-code structures at such positions. findLabels(buffer); @@ -153,6 +145,13 @@ } } } + + // Parse any associated debug information. + if (debugInfoOff != 0) { + ByteBuffer buf = dex.getDataBuffer(debugInfoOff); + debugInfo = DebugInfo.parse(buf, dex, this); + } else + debugInfo = null; } /** @@ -196,6 +195,10 @@ visitor.visitLocalVariable(local.name, local.type, local.startLabel, local.endLabel, local.regNum); + // Visit line number information if available. + if (debugInfo != null) + for (LineNumber line : debugInfo.getLineNumbers()) + visitor.visitLineNumber(line.source, line.line, line.label); } /** @@ -237,13 +240,6 @@ if ((label = labelMap.get(pos)) != null) v.visitLabel(label); - // Visit a line number entry at the current position. - if (label instanceof LineNumberLabel) { - LineNumberLabel line = (LineNumberLabel) label; - // XXX Correctly pass source file as well. - v.visitLineNumber(null, line.line, label); - } - // Switch over all possible opcodes. switch (op) { case NOP: @@ -832,7 +828,7 @@ * @param label The label to be associated with that position. * @throws DexParseException In case there already is a label. */ - void putLabel(int pos, Label label) { + private void putLabel(int pos, Label label) { if (pos < 0 || pos >= insnsSize) throw new DexParseException("Label position out of range: " + pos); if (labelMap.containsKey(pos)) @@ -924,10 +920,6 @@ // Syntax: op vAA, +BBBBBBBB off = (s2 & 0xffff) | (s3 << 16); buffer.position(buffer.position() + (pos + off) * 2); - // XXX This is a hack ---> - if (labelMap.get(pos + off) instanceof LineNumberLabel) - labelMap.remove(pos + off); - // XXX <--- This is a hack. putLabel(pos + off, new PackedSwitchLabel(buffer, pos)); buffer.reset(); break; @@ -937,10 +929,6 @@ // Syntax: op vAA, +BBBBBBBB off = (s2 & 0xffff) | (s3 << 16); buffer.position(buffer.position() + (pos + off) * 2); - // XXX This is a hack ---> - if (labelMap.get(pos + off) instanceof LineNumberLabel) - labelMap.remove(pos + off); - // XXX <--- This is a hack. putLabel(pos + off, new SparseSwitchLabel(buffer, pos)); buffer.reset(); break; @@ -961,19 +949,11 @@ */ private boolean skipInCodeData(ShortBuffer insns, int pos) { Label label = labelMap.get(pos); - if (label instanceof PackedSwitchLabel) { - insns.position(pos + ((PackedSwitchLabel) label).length()); - return true; - } - if (label instanceof SparseSwitchLabel) { - insns.position(pos + ((SparseSwitchLabel) label).length()); + if (label instanceof InCodeDataLabel) { + insns.position(pos + ((InCodeDataLabel) label).length()); return true; - } - if (label instanceof FillArrayDataLabel) { - insns.position(pos + ((FillArrayDataLabel) label).length()); - return true; - } - return false; + } else + return false; } /** @@ -1000,7 +980,7 @@ /** * In-code data structure for packed-switch instructions. */ - private class PackedSwitchLabel extends Label { + private class PackedSwitchLabel extends InCodeDataLabel { private final int size; final int firstKey; final Label[] targets; @@ -1015,20 +995,16 @@ targets[i] = putLabel(pos + buffer.getInt(), true); } + @Override public int length() { return (size * 2) + 4; } - - @Override - public boolean isJumpTarget() { - return false; - } }; /** * In-code data structure for sparse-switch instructions. */ - private class SparseSwitchLabel extends Label { + private class SparseSwitchLabel extends InCodeDataLabel { private final int size; final int[] keys; final Label[] targets; @@ -1043,20 +1019,16 @@ targets[i] = putLabel(pos + buffer.getInt(), true); } + @Override public int length() { return (size * 4) + 2; } - - @Override - public boolean isJumpTarget() { - return false; - } }; /** * In-code data structure for fill-array-data instructions. */ - private static class FillArrayDataLabel extends Label { + private static class FillArrayDataLabel extends InCodeDataLabel { final int elementWidth; final int size; final ByteBuffer data; @@ -1069,9 +1041,18 @@ data = (ByteBuffer) buffer.slice().limit(size * elementWidth); } + @Override public int length() { return (size * elementWidth + 1) / 2 + 4; } + }; + + /** + * A label as defined by the interface, but representing in-code data of a + * particular length that can be skipped. + */ + static abstract class InCodeDataLabel extends Label { + public abstract int length(); @Override public boolean isJumpTarget() { @@ -1083,7 +1064,7 @@ * A label as defined by the interface, but enriched with additional debug * information. */ - static class DebugLabel extends Label { + private static class DebugLabel extends Label { private final int pos; private boolean jumpTarget;
--- a/src/main/java/org/icedrobot/daneel/dex/DebugInfo.java Mon Mar 28 22:11:23 2011 +0200 +++ b/src/main/java/org/icedrobot/daneel/dex/DebugInfo.java Mon Mar 28 22:59:38 2011 +0200 @@ -41,7 +41,7 @@ import java.util.LinkedList; import java.util.List; -import org.icedrobot.daneel.dex.Code.DebugLabel; +import org.icedrobot.daneel.dex.Code.InCodeDataLabel; import org.icedrobot.daneel.util.BufferUtil; import org.icedrobot.daneel.util.TypeUtil; @@ -88,6 +88,8 @@ private final List<LocalVariable> localVariables; + private final List<LineNumber> lineNumbers; + private DebugInfo(ByteBuffer buffer, DexFile dex, Code code) { this.dex = dex; this.code = code; @@ -105,6 +107,7 @@ // Interpret all subsequent state machine bytecodes to compute local // variable and line number information. localVariables = new LinkedList<LocalVariable>(); + lineNumbers = new LinkedList<LineNumber>(); interpret(buffer); } @@ -124,13 +127,25 @@ * Returns the list of all local variable information as it was emitted by * the byte-coded state machine. * - * @return The list as specified above. + * @return The list as specified above, never {@code null}. */ public List<LocalVariable> getLocalVariables() { return localVariables; } /** + * Returns the list of all line numbers as emitted by the byte-coded state + * machine. Entries are sorted by instruction address low-to-high. Every + * entry denotes the original source file and line for the instructions + * following the given label. + * + * @return The list as specified above, never {@code null}. + */ + public List<LineNumber> getLineNumbers() { + return lineNumbers; + } + + /** * Interprets the byte-coded state machine that is part of a {@code * debug_info_item} and emits all the local variable and line number * information. @@ -142,7 +157,7 @@ // The five state machine registers. int addr = 0; int line = lineStart; - // We ignore "sourceFile" for now. + String sourceFile = null; // We ignore "prologueEnd" for now. // We ignore "epilogueBegin" for now. @@ -241,15 +256,15 @@ break; case DBG_SET_FILE: - // nameIdx = BufferUtil.getULEB128(buffer) - 1; - // break; - throw new UnsupportedOperationException("Not yet implemented!"); + nameIdx = BufferUtil.getULEB128(buffer) - 1; + sourceFile = resolveString(nameIdx); + break; default: int adjustedOpcode = opcode - DBG_FIRST_SPECIAL; line += (adjustedOpcode % 15) - 4; addr += (adjustedOpcode / 15); - emitLineNumber(line, addr); + emitLineNumber(line, sourceFile, addr); break; } } @@ -303,22 +318,19 @@ } /** - * Emits a new line number entry. Line numbers are stored as part of the - * label they are associated with. + * Emits a new line number entry. Every entry denotes the original source + * file and line for the instructions following the given offset. * * @param line The given line number. + * @param source The source file name the line number makes reference to. * @param addr The given instruction offset in 16-bit code units. */ - private void emitLineNumber(int line, int addr) { - Label label = new LineNumberLabel(addr, line); - try { - code.putLabel(addr, label); - } catch (DexParseException e) { - // XXX This can happen in case two line numbers are at the same - // address. Think about how to solve, fix it and remove this - // try-catch guard. - System.out.printf("DUPLICATE: line=%d, address=%d\n", line, addr); - } + private void emitLineNumber(int line, String source, int addr) { + Label label = code.putLabel(addr, false); + if (label instanceof InCodeDataLabel) + return; + LineNumber lineNumber = new LineNumber(line, source, label); + lineNumbers.add(lineNumber); } /** @@ -344,20 +356,18 @@ }; /** - * A label as defined by the interface, but enriched with additional debug - * and line number information. + * An internal representation of line number information as emitted by the + * {@code debug_info_item} state machine. */ - static class LineNumberLabel extends DebugLabel { + static class LineNumber { final int line; + final String source; + final Label label; - public LineNumberLabel(int pos, int line) { - super(pos, false); + public LineNumber(int line, String source, Label label) { this.line = line; - } - - @Override - public String toString() { - return String.format("%s[line=%d]", super.toString(), line); + this.source = source; + this.label = label; } }; }
--- a/src/main/java/org/icedrobot/daneel/rewriter/DexRewriter.java Mon Mar 28 22:11:23 2011 +0200 +++ b/src/main/java/org/icedrobot/daneel/rewriter/DexRewriter.java Mon Mar 28 22:59:38 2011 +0200 @@ -327,7 +327,7 @@ @Override public void visitLineNumber(String source, int line, Label start) { - // XXX Ignore debug information for now. + mv.visitLineNumber(line, getASMLabel(start)); } @Override