mirror of
https://github.com/revanced/smali.git
synced 2025-05-04 16:44:25 +02:00
- 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:
parent
edb11ae910
commit
698a11d2c0
@ -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,17 +639,51 @@ 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'
|
||||
| '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'
|
||||
| 'final';
|
||||
| 'declared-synchronized';
|
||||
|
||||
|
||||
|
||||
fragment MEMBER_NAME_EMIT
|
||||
: MEMBER_NAME {emit($MEMBER_NAME, MEMBER_NAME);};
|
||||
|
@ -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)))});
|
||||
|
||||
|
@ -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*);
|
||||
|
||||
|
@ -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,43 +123,148 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public void readFrom(Input in) {
|
||||
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 CONSTRUCTOR = 0x10000;
|
||||
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) {
|
||||
|
40
src/main/java/org/JesusFreke/dexlib/util/Pair.java
Normal file
40
src/main/java/org/JesusFreke/dexlib/util/Pair.java
Normal 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;
|
||||
}
|
||||
}
|
||||
|
331
src/main/java/org/JesusFreke/dexlib/util/TryListBuilder.java
Normal file
331
src/main/java/org/JesusFreke/dexlib/util/TryListBuilder.java
Normal 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);
|
||||
}
|
||||
}
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user