Merged in DebugInfoBuilder.java from the prior dexlib, and fixed it up to work with the new dexlib

git-svn-id: https://smali.googlecode.com/svn/trunk@407 55b6fa8a-2a1e-11de-a435-ffa8d773f76a
This commit is contained in:
JesusFreke@JesusFreke.com 2009-08-23 06:28:54 +00:00
parent 3080fb1bfc
commit 11503ec26e

View File

@ -0,0 +1,451 @@
/*
* [The "BSD licence"]
* Copyright (c) 2009 Ben Gruver
* 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.Util;
import org.jf.dexlib.*;
import java.util.ArrayList;
import java.util.List;
/**
* This class is intended to provide an easy to use container to build up a method's debug info. You can easily add
* an "event" at a specific address, where an event is something like a line number, start/end local, etc.
* The events must be added such that the code addresses increase monotonically. This matches how a parser would
* generally behave, and is intended to increase performance.
*/
public class DebugInfoBuilder
{
private static final int LINE_BASE = -4;
private static final int LINE_RANGE = 15;
private static final int FIRST_SPECIAL = 0x0a;
private int lineStart = 0;
private ArrayList<String> parameterNames = new ArrayList<String>();
private ArrayList<Event> events = new ArrayList<Event>();
private int lastAddress = 0;
private boolean hasData;
private int currentAddress;
private int currentLine;
public DebugInfoBuilder() {
}
private void checkAddress(int address) {
if (lastAddress > address) {
throw new RuntimeException("Cannot add an event with an address before the address of the prior event");
}
}
public void addParameterName(String parameterName) {
if (parameterName != null) {
hasData = true;
}
parameterNames.add(parameterName);
}
public void addLine(int address, int line) {
hasData = true;
checkAddress(address);
if (lineStart == 0) {
lineStart = line;
}
events.add(new LineEvent(address, line));
}
public void addLocal(int address, int registerNumber, String localName, String localType) {
hasData = true;
checkAddress(address);
events.add(new StartLocalEvent(address, registerNumber, localName, localType));
}
public void addLocalExtended(int address, int registerNumber, String localName, String localType,
String signature) {
hasData = true;
checkAddress(address);
events.add(new StartLocalExtendedEvent(address, registerNumber, localName, localType, signature));
}
public void addEndLocal(int address, int registerNumber) {
hasData = true;
checkAddress(address);
events.add(new EndLocalEvent(address, registerNumber));
}
public void addRestartLocal(int address, int registerNumber) {
hasData = true;
checkAddress(address);
events.add(new RestartLocalEvent(address, registerNumber));
}
public void addPrologue(int address) {
hasData = true;
checkAddress(address);
events.add(new PrologueEvent(address));
}
public void addEpilogue(int address) {
hasData = true;
checkAddress(address);
events.add(new EpilogueEvent(address));
}
public void addSetFile(int address, String fileName) {
hasData = true;
checkAddress(address);
events.add(new SetFileEvent(address, fileName));
}
public int getParameterNameCount() {
return parameterNames.size();
}
public DebugInfoItem encodeDebugInfo(DexFile dexFile) {
if (!hasData) {
return null;
}
ByteArrayOutput out = new ByteArrayOutput();
StringIdItem[] parameterNamesArray = new StringIdItem[parameterNames.size()];
ArrayList<Item> referencedItems = new ArrayList<Item>();
if (lineStart == 0) {
lineStart = 1;
}
currentLine = lineStart;
for (Event event: events) {
event.emit(dexFile, out, referencedItems);
}
emitEndSequence(out);
int index = 0;
for (String parameterName: parameterNames) {
if (parameterName == null) {
parameterNamesArray[index++] = null;
} else {
parameterNamesArray[index++] = StringIdItem.getInternedStringIdItem(dexFile, parameterName);
}
}
Item[] referencedItemsArray = new Item[referencedItems.size()];
referencedItems.toArray(referencedItemsArray);
return DebugInfoItem.getInternedDebugInfoItem(dexFile, lineStart, parameterNamesArray, out.getArray(),
referencedItemsArray);
}
private interface Event
{
int getAddress();
void emit(DexFile dexFile, Output out, List<Item> referencedItems);
}
private void emitEndSequence(Output out) {
out.writeByte(0);
}
private void emitAdvancePC(Output out, int address) {
int addressDelta = address-currentAddress;
if (addressDelta > 0) {
out.writeByte(1);
out.writeUnsignedLeb128(addressDelta);
currentAddress = address;
}
}
private void emitAdvanceLine(Output out, int lineDelta) {
out.writeByte(2);
out.writeSignedLeb128(lineDelta);
}
private void emitStartLocal(Output out, int registerNum) {
out.writeByte(3);
out.writeUnsignedLeb128(registerNum);
out.writeByte(1);
out.writeByte(1);
}
private void emitStartLocalExtended(Output out, int registerNum) {
out.writeByte(4);
out.writeUnsignedLeb128(registerNum);
out.writeByte(1);
out.writeByte(1);
out.writeByte(1);
}
private void emitEndLocal(Output out, int registerNum) {
out.writeByte(5);
out.writeUnsignedLeb128(registerNum);
}
private void emitRestartLocal(Output out, int registerNum) {
out.writeByte(6);
out.writeUnsignedLeb128(registerNum);
}
private void emitSetPrologueEnd(Output out) {
out.writeByte(7);
}
private void emitSetEpilogueBegin(Output out) {
out.writeByte(8);
}
private void emitSetFile(Output out) {
out.writeByte(9);
out.writeByte(1);
}
private void emitSpecialOpcode(Output out, byte opcode) {
out.writeByte(opcode);
}
private class LineEvent implements Event
{
private final int address;
private final int line;
public LineEvent(int address, int line) {
this.address = address;
this.line = line;
}
public int getAddress() {
return address;
}
public void emit(DexFile dexFile, Output out, List<Item> referencedItems) {
int lineDelta = line - currentLine;
int addressDelta = address - currentAddress;
if (lineDelta < -4 || lineDelta > 10) {
emitAdvanceLine(out, lineDelta);
lineDelta = 0;
}
if (lineDelta < 2 && addressDelta > 16 || lineDelta > 1 && addressDelta > 15) {
emitAdvancePC(out, addressDelta);
addressDelta = 0;
}
//TODO: need to handle the case when the line delta is larger than a signed int
emitSpecialOpcode(out, calculateSpecialOpcode(lineDelta, addressDelta));
currentAddress = address;
currentLine = line;
}
private byte calculateSpecialOpcode(int lineDelta, int addressDelta) {
return (byte)(FIRST_SPECIAL + (addressDelta * LINE_RANGE) + (lineDelta - LINE_BASE));
}
}
private class StartLocalEvent implements Event
{
private final int address;
private final int registerNum;
private final String localName;
private final String localType;
public StartLocalEvent(int address, int registerNum, String localName, String localType) {
this.address = address;
this.registerNum = registerNum;
this.localName = localName;
this.localType = localType;
}
public int getAddress() {
return address;
}
public void emit(DexFile dexFile, Output out, List<Item> referencedItems) {
emitAdvancePC(out, address);
emitStartLocal(out, registerNum);
referencedItems.add(localName==null?null:StringIdItem.getInternedStringIdItem(dexFile, localName));
referencedItems.add(localType==null?null:TypeIdItem.getInternedTypeIdItem(dexFile,
StringIdItem.getInternedStringIdItem(dexFile, localType)));
}
}
private class StartLocalExtendedEvent implements Event
{
private final int address;
private final int registerNum;
private final String localName;
private final String localType;
private final String signature;
public StartLocalExtendedEvent(int address, int registerNum, String localName, String localType,
String signature) {
this.address = address;
this.registerNum = registerNum;
this.localName = localName;
this.localType = localType;
this.signature = signature;
}
public int getAddress() {
return address;
}
public void emit(DexFile dexFile, Output out, List<Item> referencedItems) {
emitAdvancePC(out, address);
emitStartLocalExtended(out, registerNum);
if (localName != null) {
referencedItems.add(StringIdItem.getInternedStringIdItem(dexFile, localName));
}
if (localType != null) {
referencedItems.add(TypeIdItem.getInternedTypeIdItem(dexFile,
StringIdItem.getInternedStringIdItem(dexFile, localType)));
}
if (signature != null) {
referencedItems.add(StringIdItem.getInternedStringIdItem(dexFile, signature));
}
}
}
private class EndLocalEvent implements Event
{
private final int address;
private final int registerNum;
public EndLocalEvent(int address, int registerNum) {
this.address = address;
this.registerNum = registerNum;
}
public int getAddress() {
return address;
}
public void emit(DexFile dexFile, Output out, List<Item> referencedItems) {
emitAdvancePC(out, address);
emitEndLocal(out, registerNum);
}
}
private class RestartLocalEvent implements Event
{
private final int address;
private final int registerNum;
public RestartLocalEvent(int address, int registerNum) {
this.address = address;
this.registerNum = registerNum;
}
public int getAddress() {
return address;
}
public void emit(DexFile dexFile, Output out, List<Item> referencedItems) {
emitAdvancePC(out, address);
emitRestartLocal(out, registerNum);
}
}
private class PrologueEvent implements Event
{
private final int address;
public PrologueEvent(int address) {
this.address = address;
}
public int getAddress() {
return address;
}
public void emit(DexFile dexFile, Output out, List<Item> referencedItems) {
emitAdvancePC(out, address);
emitSetPrologueEnd(out);
}
}
private class EpilogueEvent implements Event
{
private final int address;
public EpilogueEvent(int address) {
this.address = address;
}
public int getAddress() {
return address;
}
public void emit(DexFile dexFile, Output out, List<Item> referencedItems) {
emitAdvancePC(out, address);
emitSetEpilogueBegin(out);
}
}
private class SetFileEvent implements Event
{
private final int address;
private final String fileName;
public SetFileEvent(int address, String fileName) {
this.address = address;
this.fileName = fileName;
}
public int getAddress() {
return address;
}
public void emit(DexFile dexFile, Output out, List<Item> referencedItems) {
emitAdvancePC(out, address);
emitSetFile(out);
if (fileName != null) {
referencedItems.add(StringIdItem.getInternedStringIdItem(dexFile, fileName));
}
}
}
}