- Added differentiated parsing for the 3 different types of access flags (class, method, field)

- Added support for nearly all access flags (except for the class access flags that are only applicable to inner class annotations)
- Added support for try/catch blocks


git-svn-id: https://smali.googlecode.com/svn/trunk@38 55b6fa8a-2a1e-11de-a435-ffa8d773f76a
This commit is contained in:
JesusFreke@JesusFreke.com 2009-05-14 02:02:52 +00:00
parent edb11ae910
commit 698a11d2c0
9 changed files with 733 additions and 35 deletions

View File

@ -98,6 +98,10 @@ it would emit a token for each of its children tokens.*/
lexer grammar smaliLexer;
tokens {
ACCESS_SPEC;
}
@lexer::header {
package org.JesusFreke.smali;
@ -192,11 +196,10 @@ throw e;
}
}*/
CLASS_PHRASE
: CLASS_DIRECTIVE_EMIT
WS
(ACCESS_SPEC_EMIT WS)+
(CLASS_ACCESS_SPEC_EMIT WS)+
CLASS_DESCRIPTOR_EMIT;
SUPER_PHRASE
@ -207,7 +210,7 @@ SUPER_PHRASE
FIELD_PHRASE
: FIELD_DIRECTIVE_EMIT
WS
(ACCESS_SPEC_EMIT WS)+
(FIELD_ACCESS_SPEC_EMIT WS)+
MEMBER_NAME_EMIT
WS
FIELD_TYPE_DESCRIPTOR_EMITCHILD
@ -217,7 +220,7 @@ FIELD_PHRASE
METHOD_PHRASE
: METHOD_DIRECTIVE_EMIT
WS
(ACCESS_SPEC_EMIT WS)+
(METHOD_ACCESS_SPEC_EMIT WS)+
MEMBER_NAME_EMIT
METHOD_PROTOTYPE_EMITCHILDREN;
@ -450,6 +453,19 @@ SPARSE_SWITCH_PHRASE
WSC
END_SPARSE_SWITCH_DIRECTIVE_EMIT;
CATCH_PHRASE
: CATCH_DIRECTIVE_EMIT
WS
FIELD_TYPE_DESCRIPTOR_EMITCHILD
WS 'from' WS
(LABEL_EMIT | OFFSET_EMIT)
WS 'to' WS
(LABEL_EMIT | OFFSET_EMIT)
WS 'using'
(LABEL_EMIT | OFFSET_EMIT);
//TODO: add support for both relative and absolute offsets?
fragment OFFSET_EMIT
: OFFSET {emit($OFFSET, OFFSET);};
fragment OFFSET
@ -520,6 +536,11 @@ fragment END_SPARSE_SWITCH_DIRECTIVE_EMIT
fragment END_SPARSE_SWITCH_DIRECTIVE
: '.end sparse-switch';
fragment CATCH_DIRECTIVE_EMIT
: CATCH_DIRECTIVE {emit($CATCH_DIRECTIVE, CATCH_DIRECTIVE);};
fragment CATCH_DIRECTIVE
: '.catch';
fragment REGISTER_EMIT
: REGISTER {emit($REGISTER, REGISTER);};
fragment REGISTER
@ -618,16 +639,50 @@ fragment ARRAY_CHAR_LIST[int maxCount]
;
fragment ACCESS_SPEC_EMIT
: ACCESS_SPEC {emit($ACCESS_SPEC, ACCESS_SPEC);};
fragment CLASS_ACCESS_SPEC_EMIT
: CLASS_ACCESS_SPEC {emit($CLASS_ACCESS_SPEC, ACCESS_SPEC);};
fragment ACCESS_SPEC
//TODO: add ACC_ANNOTATION when adding support for annotations
fragment CLASS_ACCESS_SPEC
: 'public'
| 'final'
| 'interface'
| 'abstract'
| 'synthetic'
| 'enum';
fragment FIELD_ACCESS_SPEC_EMIT
: FIELD_ACCESS_SPEC {emit($FIELD_ACCESS_SPEC, ACCESS_SPEC);};
fragment FIELD_ACCESS_SPEC
: 'public'
| 'private'
| 'protected'
| 'static'
| 'constructor'
| 'final';
| 'final'
| 'volatile'
| 'transient'
| 'synthetic'
| 'enum';
fragment METHOD_ACCESS_SPEC_EMIT
: METHOD_ACCESS_SPEC {emit($METHOD_ACCESS_SPEC, ACCESS_SPEC);};
fragment METHOD_ACCESS_SPEC
: 'public'
| 'private'
| 'protected'
| 'static'
| 'final'
| 'synchronized'
| 'bridge'
| 'varargs'
| 'native'
| 'abstract'
| 'strictfp'
| 'synthetic'
| 'constructor'
| 'declared-synchronized';
fragment MEMBER_NAME_EMIT

View File

@ -60,6 +60,9 @@ tokens {
I_SPARSE_SWITCH_KEYS;
I_SPARSE_SWITCH_TARGET_COUNT;
I_SPARSE_SWITCH_TARGETS;
I_CATCH;
I_CATCHES;
I_CATCH_ADDRESS;
I_STATEMENTS;
I_STATEMENT_FORMAT10t;
I_STATEMENT_FORMAT10x;
@ -151,9 +154,14 @@ statements
scope {int currentAddress;}
: {$statements::currentAddress = 0;}
( instruction {$statements::currentAddress += $instruction.size/2;}
| catch_directive
| label)*
-> ^(I_LABELS label*) ^(I_STATEMENTS instruction*);
-> ^(I_LABELS label*) ^(I_STATEMENTS instruction*) ^(I_CATCHES catch_directive*);
catch_directive
: CATCH_DIRECTIVE field_type_descriptor from=offset_or_label to=offset_or_label using=offset_or_label
-> ^(I_CATCH[$start, "I_CATCH"] I_CATCH_ADDRESS[$start, Integer.toString($statements::currentAddress)] field_type_descriptor $from $to $using)
;
label
: LABEL -> ^(I_LABEL LABEL {new CommonTree(new CommonToken(INTEGER_LITERAL,Integer.toString($statements::currentAddress)))});

View File

@ -263,13 +263,15 @@ method returns[ClassDataItem.EncodedMethod encodedMethod]
scope
{
HashMap<String, Integer> labels;
TryListBuilder tryList;
int currentAddress;
}
: {
$method::labels = new HashMap<String, Integer>();
$method::tryList = new TryListBuilder();
$method::currentAddress = 0;
}
^(I_METHOD method_name_and_prototype access_list registers_directive labels statements)
^(I_METHOD method_name_and_prototype access_list registers_directive labels statements catches)
{
MethodIdItem methodIdItem = $method_name_and_prototype.methodIdItem;
int registers = $registers_directive.registers;
@ -277,7 +279,11 @@ method returns[ClassDataItem.EncodedMethod encodedMethod]
boolean isStatic = (access & AccessFlags.STATIC) != 0;
ArrayList<Instruction> instructions = $statements.instructions;
CodeItem codeItem = new CodeItem(dexFile, registers, methodIdItem.getParameterWordCount(isStatic), instructions);
Pair<List<CodeItem.TryItem>, List<CodeItem.EncodedCatchHandler>> temp = $method::tryList.encodeTries(dexFile);
List<CodeItem.TryItem> tries = temp.first;
List<CodeItem.EncodedCatchHandler> handlers = temp.second;
CodeItem codeItem = new CodeItem(dexFile, registers, methodIdItem.getParameterWordCount(isStatic), instructions, tries, handlers);
$encodedMethod = new ClassDataItem.EncodedMethod(dexFile, methodIdItem, access, codeItem);
};
@ -337,6 +343,19 @@ fully_qualified_field returns[FieldIdItem fieldIdItem]
$fieldIdItem = new FieldIdItem(dexFile, classType, fieldName, fieldType);
};
catches : ^(I_CATCHES catch_directive*);
catch_directive
: ^(I_CATCH I_CATCH_ADDRESS field_type_descriptor from=offset_or_label to=offset_or_label using=offset_or_label)
{
TypeIdItem type = $field_type_descriptor.type;
int startAddress = $from.offsetValue + $method::currentAddress;
int endAddress = $to.offsetValue + $method::currentAddress;
int handlerAddress = $using.offsetValue + $method::currentAddress;
$method::tryList.addHandler(type, startAddress, endAddress, handlerAddress);
};
labels
: ^(I_LABELS label_def*);

View File

@ -35,6 +35,8 @@ import org.JesusFreke.dexlib.util.Input;
import org.JesusFreke.dexlib.util.Output;
import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
public class CodeItem extends OffsettedItem<CodeItem> {
private final Field[] fields;
@ -69,7 +71,7 @@ public class CodeItem extends OffsettedItem<CodeItem> {
padding = new PaddingField(),
tries = new FieldListField<TryItem>(tryItems) {
protected TryItem make() {
return new TryItem();
return new TryItem(catchHandlers);
}
},
@ -79,12 +81,22 @@ public class CodeItem extends OffsettedItem<CodeItem> {
public CodeItem(final DexFile dexFile, int registersCount, int inArguments, ArrayList<Instruction> instructions) {
public CodeItem(final DexFile dexFile, int registersCount, int inArguments, ArrayList<Instruction> instructions, List<TryItem> tries, List<EncodedCatchHandler> handlers) {
super(-1);
this.instructionList = new ArrayList<Instruction>(instructions);
this.instructionListField = new InstructionListField(dexFile);
if (tries != null) {
tryItems.addAll(tries);
if (handlers == null) {
throw new RuntimeException("The handlers parameter cannot be null if tries parameter is not null");
}
catchHandlerList.addAll(handlers);
} else if (handlers != null) {
throw new RuntimeException("The handlers parameter must be null if the tries parameter is null");
}
fields = new Field[] {
this.registersCount = new ShortIntegerField(registersCount),
this.inArgumentCount = new ShortIntegerField(inArguments),
@ -96,7 +108,7 @@ public class CodeItem extends OffsettedItem<CodeItem> {
this.padding = new PaddingField(),
this.tries = new FieldListField<TryItem>(tryItems) {
protected TryItem make() {
return new TryItem();
return new TryItem(catchHandlers);
}
},
this.catchHandlers = new EncodedCatchHandlerList(dexFile)
@ -111,41 +123,146 @@ public class CodeItem extends OffsettedItem<CodeItem> {
return ItemType.TYPE_CODE_ITEM;
}
public void copyTo(DexFile dexFile, CodeItem copy)
{
Field[] fields = getFields();
Field[] fieldsCopy = copy.getFields();
for (int i = 0; i < fields.length-2; i++) {
fields[i].copyTo(dexFile, fieldsCopy[i]);
}
//we need to do this in reverse order, so when the tries are copied,
//the catchHandler copies will already exist
catchHandlers.copyTo(dexFile, copy.catchHandlers);
tries.copyTo(dexFile, copy.tries);
}
public Field[] getFields() {
return fields;
}
public class TryItem extends CompositeField<TryItem> {
public static class TryItem extends CompositeField<TryItem> {
private final Field[] fields;
public TryItem() {
private final IntegerField startAddr;
private final ShortIntegerField insnCount;
private final EncodedCatchHandlerReference encodedCatchHandlerReference;
public TryItem(EncodedCatchHandlerList encodedCatchHandlerList) {
fields = new Field[] {
new IntegerField(),
new ShortIntegerField(),
new ShortIntegerField()
startAddr = new IntegerField(),
insnCount = new ShortIntegerField(),
encodedCatchHandlerReference = new EncodedCatchHandlerReference(encodedCatchHandlerList)
};
}
public TryItem(int startAddr, int insnCount, EncodedCatchHandler encodedCatchHandler) {
fields = new Field[] {
this.startAddr = new IntegerField(startAddr),
this.insnCount = new ShortIntegerField(insnCount),
this.encodedCatchHandlerReference = new EncodedCatchHandlerReference(encodedCatchHandler)
};
}
protected Field[] getFields() {
return fields;
}
}
class EncodedCatchHandlerList extends CompositeField<EncodedCatchHandlerList> {
public static class EncodedCatchHandlerReference extends ShortIntegerField {
private final EncodedCatchHandlerList encodedCatchHandlerList;
private EncodedCatchHandler encodedCatchHandler;
public EncodedCatchHandlerReference(EncodedCatchHandlerList encodedCatchHandlerList) {
this.encodedCatchHandlerList = encodedCatchHandlerList;
}
public EncodedCatchHandlerReference(EncodedCatchHandler encodedCatchHandler) {
this.encodedCatchHandlerList = null;
this.encodedCatchHandler = encodedCatchHandler;
}
public EncodedCatchHandlerList getEncodedCatchHandlerList() {
return encodedCatchHandlerList;
}
private void setReference(EncodedCatchHandler encodedCatchHandler) {
this.encodedCatchHandler = encodedCatchHandler;
}
public void copyTo(DexFile dexFile, CachedIntegerValueField _copy) {
EncodedCatchHandlerReference copy = (EncodedCatchHandlerReference)_copy;
EncodedCatchHandler copiedItem = copy.getEncodedCatchHandlerList().getByOffset(
encodedCatchHandler.getOffsetInList());
copy.setReference(copiedItem);
}
public void writeTo(Output out) {
cacheValue(encodedCatchHandler.getOffsetInList());
super.writeTo(out);
}
public void readFrom(Input in) {
super.readFrom(in);
encodedCatchHandler = encodedCatchHandlerList.getByOffset(getCachedValue());
}
public int place(int offset) {
cacheValue(encodedCatchHandler.getOffsetInList());
return super.place(offset);
}
}
public class EncodedCatchHandlerList extends CompositeField<EncodedCatchHandlerList> {
private boolean fieldPresent = false;
protected HashMap<Integer, EncodedCatchHandler> itemsByOffset =
new HashMap<Integer, EncodedCatchHandler>();
private final DexFile dexFile;
public EncodedCatchHandler getByOffset(int offset) {
EncodedCatchHandler encodedCatchHandler = itemsByOffset.get(offset);
if (encodedCatchHandler == null) {
encodedCatchHandler = new EncodedCatchHandler(dexFile, offset);
itemsByOffset.put(offset, encodedCatchHandler);
}
return encodedCatchHandler;
}
public EncodedCatchHandlerList(DexFile dexFile) {
this.dexFile = dexFile;
}
private final ListSizeField sizeField;
private final FieldListField<EncodedCatchHandler> listField;
private final Field[] fields = new Field[] {
new ListSizeField(catchHandlerList, new Leb128Field()),
new FieldListField<EncodedCatchHandler>(catchHandlerList) {
sizeField = new ListSizeField(catchHandlerList, new Leb128Field()),
listField = new FieldListField<EncodedCatchHandler>(catchHandlerList) {
protected EncodedCatchHandler make() {
return new EncodedCatchHandler(dexFile);
return new EncodedCatchHandler(dexFile, 0);
}
public void readFrom(Input in) {
int currentOffset = sizeField.place(0);
for (int i = 0; i < list.size(); i++) {
EncodedCatchHandler field = list.get(i);
if (field == null) {
field = itemsByOffset.get(currentOffset);
if (field == null) {
field = new EncodedCatchHandler(dexFile, currentOffset);
}
list.set(i, field);
}
int savedOffset = in.getCursor();
field.readFrom(in);
currentOffset += in.getCursor() - savedOffset;
}
}
}
};
@ -164,6 +281,9 @@ public class CodeItem extends OffsettedItem<CodeItem> {
}
public int place(int offset) {
for (EncodedCatchHandler encodedCatchHandler: listField.list) {
encodedCatchHandler.setBaseOffset(offset);
}
if (tryItems.size() > 0) {
fieldPresent = true;
return super.place(offset);
@ -179,17 +299,31 @@ public class CodeItem extends OffsettedItem<CodeItem> {
public void copyTo(DexFile dexFile, EncodedCatchHandlerList copy) {
super.copyTo(dexFile, copy);
copy.fieldPresent = fieldPresent;
copy.itemsByOffset.clear();
for (EncodedCatchHandler encodedCatchHandler: copy.listField.list) {
copy.itemsByOffset.put(encodedCatchHandler.offset, encodedCatchHandler);
}
}
}
public class EncodedCatchHandler extends CompositeField<EncodedCatchHandler> {
public static class EncodedCatchHandler extends CompositeField<EncodedCatchHandler> {
public final Field[] fields;
private ArrayList<EncodedTypeAddrPair> list = new ArrayList<EncodedTypeAddrPair>();
private boolean hasCatchAll = false;
private ArrayList<EncodedTypeAddrPair> list;
boolean hasCatchAll = false;
private int baseOffset = 0;
public EncodedCatchHandler(final DexFile dexFile) {
private final ListSizeField size;
private final FieldListField<EncodedTypeAddrPair> handlers;
private final Leb128Field catchAllAddress;
private int offset;
public EncodedCatchHandler(final DexFile dexFile, int offset) {
this.offset = offset;
list = new ArrayList<EncodedTypeAddrPair>();
fields = new Field[] {
new ListSizeField(list, new SignedLeb128Field() {
size = new ListSizeField(list, new SignedLeb128Field() {
public void readFrom(Input in) {
super.readFrom(in);
hasCatchAll = (getCachedValue() <= 0);
@ -199,12 +333,12 @@ public class CodeItem extends OffsettedItem<CodeItem> {
super.cacheValue(value * (hasCatchAll?-1:1));
}})
,
new FieldListField<EncodedTypeAddrPair>(list) {
handlers = new FieldListField<EncodedTypeAddrPair>(list) {
protected EncodedTypeAddrPair make() {
return new EncodedTypeAddrPair(dexFile);
}
},
new Leb128Field() {
catchAllAddress = new Leb128Field() {
public void readFrom(Input in) {
if (hasCatchAll) {
super.readFrom(in);
@ -227,17 +361,41 @@ public class CodeItem extends OffsettedItem<CodeItem> {
};
}
public EncodedCatchHandler(final DexFile dexFile, List<EncodedTypeAddrPair> handlers, int catchAllHandler) {
this(dexFile, 0);
list.addAll(handlers);
if (catchAllHandler >= 0) {
hasCatchAll = true;
catchAllAddress.cacheValue(catchAllHandler);
}
}
protected Field[] getFields() {
return fields;
}
public int getOffsetInList() {
return offset-baseOffset;
}
public void setBaseOffset(int baseOffset) {
this.baseOffset = baseOffset;
}
public void copyTo(DexFile dexFile, EncodedCatchHandler copy) {
super.copyTo(dexFile, copy);
copy.hasCatchAll = hasCatchAll;
copy.offset = offset;
}
public int place(int offset) {
this.offset = offset;
return super.place(offset);
}
}
public class EncodedTypeAddrPair extends CompositeField<EncodedTypeAddrPair> {
public static class EncodedTypeAddrPair extends CompositeField<EncodedTypeAddrPair> {
public final Field[] fields;
public EncodedTypeAddrPair(DexFile dexFile) {
@ -247,6 +405,13 @@ public class CodeItem extends OffsettedItem<CodeItem> {
};
}
public EncodedTypeAddrPair(DexFile dexFile, TypeIdItem type, int handlerOffset) {
fields = new Field[] {
new IndexedItemReference<TypeIdItem>(dexFile, type, new Leb128Field()),
new Leb128Field(handlerOffset)
};
}
protected Field[] getFields() {
return fields;
}

View File

@ -34,7 +34,7 @@ import org.JesusFreke.dexlib.util.Input;
import java.util.ArrayList;
public abstract class FieldListField<T extends Field> implements Field<FieldListField<T>> {
private final ArrayList<T> list;
final ArrayList<T> list;
public FieldListField(ArrayList<T> list) {
this.list = list;

View File

@ -34,8 +34,23 @@ public class AccessFlags
{
public static final int PUBLIC = 0x01;
public static final int PRIVATE = 0x02;
public static final int PROTECTED = 0x04;
public static final int STATIC = 0x08;
public static final int FINAL = 0x10;
public static final int SYNCHRONIZED = 0x20;
public static final int VOLATILE = 0x40;
public static final int BRIDGE = 0x40;
public static final int TRANSIENT = 0x80;
public static final int VARARGS = 0x80;
public static final int NATIVE = 0x100;
public static final int INTERFACE = 0x200;
public static final int ABSTRACT = 0x400;
public static final int STRICTFP = 0x800;
public static final int SYNTHETIC = 0x1000;
public static final int ANNOTATION = 0x2000;
public static final int ENUM = 0x4000;
public static final int CONSTRUCTOR = 0x10000;
public static final int DECLARED_SYNCHRONIZED = 0x20000;
private static HashMap<String, Integer> accessFlagValues;
@ -44,7 +59,21 @@ public class AccessFlags
accessFlagValues.put("public", PUBLIC);
accessFlagValues.put("private", PRIVATE);
accessFlagValues.put("static", STATIC);
accessFlagValues.put("final", FINAL);
accessFlagValues.put("synchronized", SYNCHRONIZED);
accessFlagValues.put("volatile", VOLATILE);
accessFlagValues.put("bridge", BRIDGE);
accessFlagValues.put("transient", TRANSIENT);
accessFlagValues.put("varargs", VARARGS);
accessFlagValues.put("native", NATIVE);
accessFlagValues.put("interface", INTERFACE);
accessFlagValues.put("abstract", ABSTRACT);
accessFlagValues.put("strictfp", STRICTFP);
accessFlagValues.put("synthetic", SYNTHETIC);
accessFlagValues.put("annotation", ANNOTATION);
accessFlagValues.put("enum", ENUM);
accessFlagValues.put("constructor", CONSTRUCTOR);
accessFlagValues.put("declared-synchronized", DECLARED_SYNCHRONIZED);
}
public static int getValueForAccessFlag(String accessFlag) {

View File

@ -0,0 +1,40 @@
/*
* [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.JesusFreke.dexlib.util;
public class Pair<A, B> {
public final A first;
public final B second;
public Pair(A first, B second) {
this.first = first;
this.second = second;
}
}

View File

@ -0,0 +1,331 @@
/*
* [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.JesusFreke.dexlib.util;
import org.JesusFreke.dexlib.TypeIdItem;
import org.JesusFreke.dexlib.CodeItem;
import org.JesusFreke.dexlib.DexFile;
import java.util.*;
public class TryListBuilder
{
//TODO: need to thoroughly test this
private TryRange firstTryRange = new TryRange(0,0);
private TryRange lastTryRange = new TryRange(0,0);
public TryListBuilder() {
firstTryRange.next = lastTryRange;
lastTryRange.previous = firstTryRange;
}
private class TryRange
{
public TryRange previous = null;
public TryRange next = null;
public int startAddress;
public int endAddress;
public LinkedList<Handler> handlers;
public int catchAllHandlerAddress;
public TryRange(int startAddress, int endAddress) {
this.startAddress = startAddress;
this.endAddress = endAddress;
this.handlers = new LinkedList<Handler>();
this.previous = null;
this.next = null;
catchAllHandlerAddress = -1;
}
public void append(TryRange tryRange) {
/*we use a dummy last item, so this.next will always
have a value*/
this.next.previous = tryRange;
tryRange.next = this.next;
this.next = tryRange;
tryRange.previous = this;
}
public void prepend(TryRange tryRange){
/*we use a dummy first item, so this.previous will always
have a value*/
this.previous.next = tryRange;
tryRange.previous = this.previous;
this.previous = tryRange;
tryRange.next = this;
}
/**
* This splits the current range into two ranges at the given
* address. The existing range will be shortened to the first
* half, and a new range will be created and returned for the
* 2nd half.
* @param address The address to split at
* @return The 2nd half of the
*/
public TryRange split(int address) {
//this is a private class, so address is assumed
//to be valid
TryRange tryRange = new TryRange(address, endAddress);
tryRange.catchAllHandlerAddress = this.catchAllHandlerAddress;
append(tryRange);
this.endAddress = address;
return tryRange;
}
public void appendHandler(Handler handler) {
handlers.addLast(handler);
}
public void prependHandler(Handler handler) {
handlers.addFirst(handler);
}
}
private class Handler
{
public final TypeIdItem type;
public final int handlerAddress;
public Handler(TypeIdItem type, int handlerAddress) {
this.type = type;
this.handlerAddress = handlerAddress;
}
}
/*public TryListBuilder(CodeItem item)
{
} */
public Pair<List<CodeItem.TryItem>, List<CodeItem.EncodedCatchHandler>> encodeTries(DexFile dexFile) {
if (firstTryRange.next == lastTryRange) {
return new Pair<List<CodeItem.TryItem>, List<CodeItem.EncodedCatchHandler>>(null, null);
}
ArrayList<CodeItem.TryItem> tries = new ArrayList<CodeItem.TryItem>();
ArrayList<CodeItem.EncodedCatchHandler> handlers = new ArrayList<CodeItem.EncodedCatchHandler>();
HashMap<CodeItem.EncodedCatchHandler, CodeItem.EncodedCatchHandler> handlerDict =
new HashMap<CodeItem.EncodedCatchHandler, CodeItem.EncodedCatchHandler>();
TryRange tryRange = firstTryRange.next;
while (tryRange != lastTryRange) {
ArrayList<CodeItem.EncodedTypeAddrPair> encodedTypeAddrPairs =
new ArrayList<CodeItem.EncodedTypeAddrPair>();
for (Handler handler: tryRange.handlers) {
CodeItem.EncodedTypeAddrPair encodedTypeAddrPair = new CodeItem.EncodedTypeAddrPair(
dexFile,
handler.type,
handler.handlerAddress);
encodedTypeAddrPairs.add(encodedTypeAddrPair);
}
CodeItem.EncodedCatchHandler encodedCatchHandler = new CodeItem.EncodedCatchHandler(
dexFile,
encodedTypeAddrPairs,
tryRange.catchAllHandlerAddress);
CodeItem.EncodedCatchHandler internedEncodedCatchHandler = handlerDict.get(encodedCatchHandler);
if (internedEncodedCatchHandler == null) {
handlerDict.put(encodedCatchHandler, encodedCatchHandler);
handlers.add(encodedCatchHandler);
} else {
encodedCatchHandler = internedEncodedCatchHandler;
}
CodeItem.TryItem tryItem = new CodeItem.TryItem(
tryRange.startAddress,
tryRange.endAddress - tryRange.startAddress,
encodedCatchHandler);
tries.add(tryItem);
tryRange = tryRange.next;
}
return new Pair<List<CodeItem.TryItem>, List<CodeItem.EncodedCatchHandler>>(tries, handlers);
}
public void addCatchAllHandler(int startAddress, int endAddress, int handlerAddress) {
TryRange startRange = null;
TryRange endRange = null;
Pair<TryRange, TryRange> ranges = getBoundingRanges(startAddress, endAddress);
startRange = ranges.first;
endRange = ranges.second;
int previousEnd = startAddress;
TryRange tryRange = startRange;
/*Now we have the start and end ranges that exactly match the start and end
of the range being added. We need to iterate over all the ranges from the start
to end range inclusively, and append the handler to the end of each range's handler
list. We also need to create a new range for any "holes" in the existing ranges*/
do
{
//is there a hole? If so, add a new range to fill the hole
if (tryRange.startAddress > previousEnd) {
TryRange newRange = new TryRange(previousEnd, tryRange.startAddress);
tryRange.prepend(newRange);
tryRange = newRange;
}
tryRange.catchAllHandlerAddress = handlerAddress;
previousEnd = tryRange.endAddress;
tryRange = tryRange.next;
} while (tryRange.previous != endRange);
}
public Pair<TryRange, TryRange> getBoundingRanges(int startAddress, int endAddress) {
TryRange startRange = null;
TryRange endRange = null;
TryRange tryRange = firstTryRange.next;
while (tryRange != lastTryRange) {
if (tryRange.startAddress == startAddress) {
//|-----|
//^------
/*Bam. We hit the start of the range right on the head*/
startRange = tryRange;
break;
} else if (tryRange.startAddress < startAddress && tryRange.endAddress > startAddress) {
//|-----|
// ^----
/*Almost. The start of the range being added is in the middle
of an existing try range. We need to split the existing range
at the start address of the range being added*/
startRange = tryRange.split(startAddress);
break;
}else if (tryRange.startAddress > startAddress) {
// |-----|
//^---------
/*Oops, too far! We've passed the start of the range being added.
We need to add a new range just before this one*/
startRange = new TryRange(startAddress, tryRange.startAddress);
tryRange.prepend(startRange);
break;
}
tryRange = tryRange.next;
}
//|-----|
// ^-----
/*Either the list of tries is blank, or all the tries in the list
end before the range being added starts. In either case, we just need
to add a new range at the end of the list*/
if (startRange == null) {
TryRange newRange = new TryRange(startAddress, endAddress);
lastTryRange.prepend(newRange);
return new Pair<TryRange, TryRange>(newRange, newRange);
}
tryRange = startRange;
while (tryRange != lastTryRange) {
if (tryRange.endAddress == endAddress) {
//|-----|
//------^
/*Bam! We hit the end right on the head.*/
endRange = tryRange;
break;
} else if (tryRange.startAddress < endAddress && tryRange.endAddress > endAddress) {
//|-----|
//--^
/*Almost. The range being added ends in the middle of an
existing range. We need to split the existing range
at the end of the range being added.*/
tryRange.split(endAddress);
endRange = tryRange;
break;
} else if (tryRange.startAddress >= endAddress) {
//|-----| |-----|
//-----------^
/*Oops, too far! The current range starts after the range being added
ends. We need to create a new range that starts at the end of the
previous range, and ends at the end of the range being added*/
endRange = new TryRange(tryRange.previous.endAddress, endAddress);
tryRange.prepend(endRange);
break;
}
tryRange = tryRange.next;
}
//|-----|
//--------^
/*The last range in the list ended before the end of the range being added.
We need to add a new range that starts at the end of the last range in the
list, and ends at the end of the range being added.*/
if (endRange == null) {
endRange = new TryRange(lastTryRange.previous.endAddress, endAddress);
lastTryRange.prepend(endRange);
}
return new Pair<TryRange, TryRange>(startRange, endRange);
}
public void addHandler(TypeIdItem type, int startAddress, int endAddress, int handlerAddress) {
TryRange startRange = null;
TryRange endRange = null;
Pair<TryRange, TryRange> ranges = getBoundingRanges(startAddress, endAddress);
startRange = ranges.first;
endRange = ranges.second;
Handler handler = new Handler(type, handlerAddress);
int previousEnd = startAddress;
TryRange tryRange = startRange;
/*Now we have the start and end ranges that exactly match the start and end
of the range being added. We need to iterate over all the ranges from the start
to end range inclusively, and append the handler to the end of each range's handler
list. We also need to create a new range for any "holes" in the existing ranges*/
do
{
//is there a hole? If so, add a new range to fill the hole
if (tryRange.startAddress > previousEnd) {
TryRange newRange = new TryRange(previousEnd, tryRange.startAddress);
tryRange.prepend(newRange);
tryRange = newRange;
}
tryRange.appendHandler(handler);
previousEnd = tryRange.endAddress;
tryRange = tryRange.next;
} while (tryRange.previous != endRange);
}
}

View File

@ -36,6 +36,8 @@
;5000000000
;5000000
;Label12
;Label13
;In the exception handler.
.method static constructor <clinit>()V ;test
@ -330,6 +332,43 @@ SparseSwitch:
.end method
.method public testTry()Ljava/lang/String;
.registers 2
;0
const-string v0, "This shouldn't be displayed!"
;2
tryStart:
new-instance v1, Ljava/lang/Exception;
;4
invoke-direct {v1}, java/lang/Exception/<init>()V
;7
throw v1
nop
nop
;8
tryEnd:
return-object v0
;9
.catch Ljava/lang/Exception; from tryStart: to tryEnd: using handler:
handler:
const-string v0, "In the exception handler."
return-object v0
.end method
.method public onCreate(Landroid/os/Bundle;)V
@ -675,6 +714,18 @@ SparseSwitch:
;test try-catch block
invoke-virtual {v4}, org/JesusFreke/HelloWorld2/HelloWorld2/testTry()Ljava/lang/String;
move-result-object v1
invoke-virtual {v2, v1}, java/lang/String/concat(Ljava/lang/String;)Ljava/lang/String;
move-result-object v2
invoke-virtual {v2, v3}, java/lang/String/concat(Ljava/lang/String;)Ljava/lang/String;
move-result-object v2
check-cast v4, Landroid/app/Activity;
invoke-virtual {v0,v2}, android/widget/TextView/setText(Ljava/lang/CharSequence;)V