blob: b61f399dbecc13bdf39407bda58cb7fc13c2d3b4 [file] [log] [blame]
/*
* [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;
}
}
}