| /* |
| * [The "BSD licence"] |
| * Copyright (c) 2010 Ben Gruver (JesusFreke) |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. The name of the author may not be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
| * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| package org.jf.dexlib.Debug; |
| |
| import org.jf.dexlib.DebugInfoItem; |
| import org.jf.dexlib.DexFile; |
| import org.jf.dexlib.StringIdItem; |
| import org.jf.dexlib.TypeIdItem; |
| import org.jf.dexlib.Util.ByteArrayInput; |
| import org.jf.dexlib.Util.Input; |
| |
| public class DebugInstructionIterator { |
| /** |
| * This method decodes the debug instructions in the given byte array and iterates over them, calling |
| * the ProcessDebugInstructionDelegate.ProcessDebugInstruction method for each instruction |
| * @param in an Input object that the debug instructions can be read from |
| * @param processDebugInstruction a <code>ProcessDebugInstructionDelegate</code> object that gets called |
| * for each instruction that is encountered |
| */ |
| public static void IterateInstructions(Input in, ProcessRawDebugInstructionDelegate processDebugInstruction) { |
| int startDebugOffset; |
| |
| while(true) |
| { |
| startDebugOffset = in.getCursor(); |
| byte debugOpcode = in.readByte(); |
| |
| switch (debugOpcode) { |
| case 0x00: |
| { |
| processDebugInstruction.ProcessEndSequence(startDebugOffset); |
| return; |
| } |
| case 0x01: |
| { |
| int codeAddressDiff = in.readUnsignedLeb128(); |
| processDebugInstruction.ProcessAdvancePC(startDebugOffset, in.getCursor() - startDebugOffset, |
| codeAddressDiff); |
| break; |
| } |
| case 0x02: |
| { |
| int lineDiff = in.readSignedLeb128(); |
| processDebugInstruction.ProcessAdvanceLine(startDebugOffset, in.getCursor() - startDebugOffset, |
| lineDiff); |
| break; |
| } |
| case 0x03: |
| { |
| int registerNum = in.readUnsignedOrSignedLeb128(); |
| boolean isSignedRegister = false; |
| if (registerNum < 0) { |
| isSignedRegister = true; |
| registerNum = ~registerNum; |
| } |
| int nameIndex = in.readUnsignedLeb128() - 1; |
| int typeIndex = in.readUnsignedLeb128() - 1; |
| processDebugInstruction.ProcessStartLocal(startDebugOffset, in.getCursor() - startDebugOffset, |
| registerNum, nameIndex, typeIndex, isSignedRegister); |
| break; |
| } |
| case 0x04: |
| { |
| int registerNum = in.readUnsignedOrSignedLeb128(); |
| boolean isSignedRegister = false; |
| if (registerNum < 0) { |
| isSignedRegister = true; |
| registerNum = ~registerNum; |
| } |
| int nameIndex = in.readUnsignedLeb128() - 1; |
| int typeIndex = in.readUnsignedLeb128() - 1; |
| int signatureIndex = in.readUnsignedLeb128() - 1; |
| processDebugInstruction.ProcessStartLocalExtended(startDebugOffset, |
| in.getCursor() - startDebugOffset, registerNum, nameIndex, typeIndex, signatureIndex, |
| isSignedRegister); |
| break; |
| } |
| case 0x05: |
| { |
| int registerNum = in.readUnsignedOrSignedLeb128(); |
| boolean isSignedRegister = false; |
| if (registerNum < 0) { |
| isSignedRegister = true; |
| registerNum = ~registerNum; |
| } |
| processDebugInstruction.ProcessEndLocal(startDebugOffset, in.getCursor() - startDebugOffset, |
| registerNum, isSignedRegister); |
| break; |
| } |
| case 0x06: |
| { |
| int registerNum = in.readUnsignedOrSignedLeb128(); |
| boolean isSignedRegister = false; |
| if (registerNum < 0) { |
| isSignedRegister = true; |
| registerNum = ~registerNum; |
| } |
| processDebugInstruction.ProcessRestartLocal(startDebugOffset, in.getCursor() - startDebugOffset, |
| registerNum, isSignedRegister); |
| break; |
| } |
| case 0x07: |
| { |
| processDebugInstruction.ProcessSetPrologueEnd(startDebugOffset); |
| break; |
| } |
| case 0x08: |
| { |
| processDebugInstruction.ProcessSetEpilogueBegin(startDebugOffset); |
| break; |
| } |
| case 0x09: |
| { |
| int nameIndex = in.readUnsignedLeb128(); |
| processDebugInstruction.ProcessSetFile(startDebugOffset, in.getCursor() - startDebugOffset, |
| nameIndex); |
| break; |
| } |
| default: |
| { |
| int base = ((debugOpcode & 0xFF) - 0x0A); |
| processDebugInstruction.ProcessSpecialOpcode(startDebugOffset, debugOpcode, (base % 15) - 4, base / 15); |
| } |
| } |
| } |
| } |
| |
| /** |
| * This method decodes the debug instructions in the given byte array and iterates over them, calling |
| * the ProcessDebugInstructionDelegate.ProcessDebugInstruction method for each instruction |
| * @param debugInfoItem the <code>DebugInfoItem</code> to iterate over |
| * @param registerCount the number of registers in the method that the given debug info is for |
| * @param processDecodedDebugInstruction a <code>ProcessDebugInstructionDelegate</code> object that gets called |
| * for each instruction that is encountered |
| */ |
| public static void DecodeInstructions(DebugInfoItem debugInfoItem, int registerCount, |
| ProcessDecodedDebugInstructionDelegate processDecodedDebugInstruction) { |
| int startDebugOffset; |
| int currentCodeAddress = 0; |
| int line = debugInfoItem.getLineStart(); |
| Input in = new ByteArrayInput(debugInfoItem.getEncodedDebugInfo()); |
| DexFile dexFile = debugInfoItem.getDexFile(); |
| |
| Local[] locals = new Local[registerCount]; |
| |
| while(true) |
| { |
| startDebugOffset = in.getCursor(); |
| byte debugOpcode = in.readByte(); |
| |
| switch (DebugOpcode.getDebugOpcodeByValue(debugOpcode)) { |
| case DBG_END_SEQUENCE: |
| { |
| return; |
| } |
| case DBG_ADVANCE_PC: |
| { |
| int codeAddressDiff = in.readUnsignedLeb128(); |
| currentCodeAddress += codeAddressDiff; |
| break; |
| } |
| case DBG_ADVANCE_LINE: |
| { |
| int lineDiff = in.readSignedLeb128(); |
| line += lineDiff; |
| break; |
| } |
| case DBG_START_LOCAL: |
| { |
| int registerNum = in.readUnsignedLeb128(); |
| StringIdItem name = dexFile.StringIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1); |
| TypeIdItem type = dexFile.TypeIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1); |
| locals[registerNum] = new Local(registerNum, name, type, null); |
| processDecodedDebugInstruction.ProcessStartLocal(currentCodeAddress, |
| in.getCursor() - startDebugOffset, registerNum, name, type); |
| break; |
| } |
| case DBG_START_LOCAL_EXTENDED: |
| { |
| int registerNum = in.readUnsignedLeb128(); |
| StringIdItem name = dexFile.StringIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1); |
| TypeIdItem type = dexFile.TypeIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1); |
| StringIdItem signature = |
| dexFile.StringIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1); |
| locals[registerNum] = new Local(registerNum, name, type, signature); |
| processDecodedDebugInstruction.ProcessStartLocalExtended(currentCodeAddress, |
| in.getCursor() - startDebugOffset, registerNum, name, type, signature); |
| break; |
| } |
| case DBG_END_LOCAL: |
| { |
| int registerNum = in.readUnsignedLeb128(); |
| Local local = locals[registerNum]; |
| if (local == null) { |
| processDecodedDebugInstruction.ProcessEndLocal(currentCodeAddress, in.getCursor() - startDebugOffset, registerNum, |
| null, null, null); |
| } else { |
| processDecodedDebugInstruction.ProcessEndLocal(currentCodeAddress, in.getCursor() - startDebugOffset, registerNum, |
| local.name, local.type, local.signature); |
| } |
| break; |
| } |
| case DBG_RESTART_LOCAL: |
| { |
| int registerNum = in.readUnsignedLeb128(); |
| Local local = locals[registerNum]; |
| if (local == null) { |
| processDecodedDebugInstruction.ProcessRestartLocal(currentCodeAddress, in.getCursor() - startDebugOffset, |
| registerNum, null, null, null); |
| } else { |
| processDecodedDebugInstruction.ProcessRestartLocal(currentCodeAddress, in.getCursor() - startDebugOffset, |
| registerNum, local.name, local.type, local.signature); |
| } |
| |
| break; |
| } |
| case DBG_SET_PROLOGUE_END: |
| { |
| processDecodedDebugInstruction.ProcessSetPrologueEnd(currentCodeAddress); |
| break; |
| } |
| case DBG_SET_EPILOGUE_BEGIN: |
| { |
| processDecodedDebugInstruction.ProcessSetEpilogueBegin(currentCodeAddress); |
| break; |
| } |
| case DBG_SET_FILE: |
| { |
| StringIdItem name = dexFile.StringIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1); |
| processDecodedDebugInstruction.ProcessSetFile(currentCodeAddress, in.getCursor() - startDebugOffset, name); |
| break; |
| } |
| case DBG_SPECIAL_OPCODE: |
| { |
| int base = ((debugOpcode & 0xFF) - 0x0A); |
| currentCodeAddress += base / 15; |
| line += (base % 15) - 4; |
| processDecodedDebugInstruction.ProcessLineEmit(currentCodeAddress, line); |
| } |
| } |
| } |
| } |
| |
| public static class ProcessRawDebugInstructionDelegate |
| { |
| //TODO: add javadocs |
| public void ProcessEndSequence(int startDebugOffset) { |
| ProcessStaticOpcode(DebugOpcode.DBG_END_SEQUENCE, startDebugOffset, 1); |
| } |
| |
| public void ProcessAdvancePC(int startDebugOffset, int length, int codeAddressDiff) { |
| ProcessStaticOpcode(DebugOpcode.DBG_ADVANCE_PC, startDebugOffset, length); |
| } |
| |
| public void ProcessAdvanceLine(int startDebugOffset, int length, int lineDiff) { |
| ProcessStaticOpcode(DebugOpcode.DBG_ADVANCE_LINE, startDebugOffset, length); |
| } |
| |
| public void ProcessStartLocal(int startDebugOffset, int length, int registerNum, int nameIndex, int typeIndex, |
| boolean registerIsSigned) { |
| } |
| |
| public void ProcessStartLocalExtended(int startDebugOffset, int length, int registerNum, int nameIndex, |
| int typeIndex,int signatureIndex, boolean registerIsSigned) { |
| } |
| |
| public void ProcessEndLocal(int startDebugOffset, int length, int registerNum, boolean registerIsSigned) { |
| ProcessStaticOpcode(DebugOpcode.DBG_END_LOCAL, startDebugOffset, length); |
| } |
| |
| public void ProcessRestartLocal(int startDebugOffset, int length, int registerNum, boolean registerIsSigned) { |
| ProcessStaticOpcode(DebugOpcode.DBG_RESTART_LOCAL, startDebugOffset, length); |
| } |
| |
| public void ProcessSetPrologueEnd(int startDebugOffset) { |
| ProcessStaticOpcode(DebugOpcode.DBG_SET_PROLOGUE_END, startDebugOffset, 1); |
| } |
| |
| public void ProcessSetEpilogueBegin(int startDebugOffset) { |
| ProcessStaticOpcode(DebugOpcode.DBG_SET_EPILOGUE_BEGIN, startDebugOffset, 1); |
| } |
| |
| public void ProcessSetFile(int startDebugOffset, int length, int nameIndex) { |
| } |
| |
| public void ProcessSpecialOpcode(int startDebugOffset, int debugOpcode, int lineDiff, int codeAddressDiff) { |
| ProcessStaticOpcode(DebugOpcode.DBG_SPECIAL_OPCODE, startDebugOffset, 1); |
| } |
| |
| public void ProcessStaticOpcode(DebugOpcode debugOpcode, int startDebugOffset, int length) { |
| } |
| } |
| |
| public static class ProcessDecodedDebugInstructionDelegate |
| { |
| public void ProcessStartLocal(int codeAddress, int length, int registerNum, StringIdItem name, |
| TypeIdItem type) { |
| } |
| |
| public void ProcessStartLocalExtended(int codeAddress, int length, int registerNum, StringIdItem name, |
| TypeIdItem type, StringIdItem signature) { |
| } |
| |
| public void ProcessEndLocal(int codeAddress, int length, int registerNum, StringIdItem name, TypeIdItem type, |
| StringIdItem signature) { |
| } |
| |
| public void ProcessRestartLocal(int codeAddress, int length, int registerNum, StringIdItem name, |
| TypeIdItem type, StringIdItem signature) { |
| } |
| |
| public void ProcessSetPrologueEnd(int codeAddress) { |
| } |
| |
| public void ProcessSetEpilogueBegin(int codeAddress) { |
| } |
| |
| public void ProcessSetFile(int codeAddress, int length, StringIdItem name) { |
| } |
| |
| public void ProcessLineEmit(int codeAddress, int line) { |
| } |
| } |
| |
| private static class Local { |
| public final int register; |
| public final StringIdItem name; |
| public final TypeIdItem type; |
| public final StringIdItem signature; |
| public Local(int register, StringIdItem name, TypeIdItem type, StringIdItem signature) { |
| this.register = register; |
| this.name = name; |
| this.type = type; |
| this.signature = signature; |
| } |
| |
| } |
| } |