mirror of
https://github.com/revanced/smali.git
synced 2025-05-04 08:34:25 +02:00

git-svn-id: https://smali.googlecode.com/svn/trunk@202 55b6fa8a-2a1e-11de-a435-ffa8d773f76a
598 lines
23 KiB
Java
598 lines
23 KiB
Java
/*
|
|
* [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;
|
|
|
|
import org.jf.dexlib.code.InstructionField;
|
|
import org.jf.dexlib.code.Opcode;
|
|
import org.jf.dexlib.util.AnnotatedOutput;
|
|
import org.jf.dexlib.util.Input;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Collections;
|
|
|
|
public class CodeItem extends OffsettedItem<CodeItem> {
|
|
private final ArrayList<InstructionField> instructionList;
|
|
private final ArrayList<TryItem> tryItems = new ArrayList<TryItem>();
|
|
private final ArrayList<EncodedCatchHandler> catchHandlerList = new ArrayList<EncodedCatchHandler>();
|
|
|
|
private final ShortIntegerField registersCountField;
|
|
private final ShortIntegerField inArgumentCountField;
|
|
private final ShortIntegerField outArgumentCountField;
|
|
private final ListSizeField triesCountField;
|
|
private final OffsettedItemReference<DebugInfoItem> debugInfoReferenceField;
|
|
private final IntegerField instructionsSizeField;
|
|
private final InstructionListField instructionListField;
|
|
private final PaddingField paddingField;
|
|
private final FieldListField<TryItem> triesListField;
|
|
private final EncodedCatchHandlerList catchHandlersListField;
|
|
|
|
public CodeItem(final DexFile dexFile, int offset) {
|
|
super(offset);
|
|
|
|
instructionList = new ArrayList<InstructionField>();
|
|
|
|
fields = new Field[] {
|
|
registersCountField = new ShortIntegerField("registers_size"),
|
|
inArgumentCountField = new ShortIntegerField("ins_size"),
|
|
outArgumentCountField = new ShortIntegerField("outs_size"),
|
|
triesCountField = new ListSizeField(tryItems, new ShortIntegerField("tries_size")),
|
|
debugInfoReferenceField = new OffsettedItemReference<DebugInfoItem>(dexFile.DebugInfoItemsSection,
|
|
new IntegerField(null), "debug_off"),
|
|
instructionsSizeField = new IntegerField("insns_size"),
|
|
instructionListField = new InstructionListField(dexFile),
|
|
paddingField = new PaddingField(),
|
|
triesListField = new FieldListField<TryItem>(tryItems, "try_item") {
|
|
protected TryItem make() {
|
|
return new TryItem(catchHandlersListField);
|
|
}
|
|
},
|
|
|
|
catchHandlersListField = new EncodedCatchHandlerList(dexFile)
|
|
};
|
|
}
|
|
|
|
public CodeItem(final DexFile dexFile,
|
|
int registersCount,
|
|
int inArguments,
|
|
List<InstructionField> instructions,
|
|
DebugInfoItem debugInfo,
|
|
List<TryItem> tries,
|
|
List<EncodedCatchHandler> handlers) {
|
|
this(dexFile, 0);
|
|
|
|
instructionList.addAll(instructions);
|
|
instructionsSizeField.cacheValue(instructionListField.getInstructionWordCount());
|
|
|
|
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");
|
|
}
|
|
|
|
registersCountField.cacheValue(registersCount);
|
|
inArgumentCountField.cacheValue(inArguments);
|
|
outArgumentCountField.cacheValue(instructionListField.getOutArguments());
|
|
debugInfoReferenceField.setReference(debugInfo);
|
|
}
|
|
|
|
protected int getAlignment() {
|
|
return 4;
|
|
}
|
|
|
|
public ItemType getItemType() {
|
|
return ItemType.TYPE_CODE_ITEM;
|
|
}
|
|
|
|
public int getRegisterCount() {
|
|
return registersCountField.getCachedValue();
|
|
}
|
|
|
|
public List<InstructionField> getInstructions() {
|
|
return Collections.unmodifiableList(instructionList);
|
|
}
|
|
|
|
public List<TryItem> getTries() {
|
|
return Collections.unmodifiableList(tryItems);
|
|
}
|
|
|
|
public DebugInfoItem getDebugInfo() {
|
|
return debugInfoReferenceField.getReference();
|
|
}
|
|
|
|
public void copyTo(DexFile dexFile, CodeItem copy)
|
|
{
|
|
for (int i = 0; i < fields.length-2; i++) {
|
|
fields[i].copyTo(dexFile, copy.fields[i]);
|
|
}
|
|
//we need to do this in reverse order, so when the tries are copied,
|
|
//the catchHandler copies will already exist
|
|
catchHandlersListField.copyTo(dexFile, copy.catchHandlersListField);
|
|
triesListField.copyTo(dexFile, copy.triesListField);
|
|
}
|
|
|
|
public String getConciseIdentity() {
|
|
//TODO: should mention the method name here
|
|
return "code_item @0x" + Integer.toHexString(getOffset());
|
|
}
|
|
|
|
public static class TryItem extends CompositeField<TryItem> {
|
|
private final IntegerField startAddr;
|
|
private final ShortIntegerField insnCount;
|
|
private final EncodedCatchHandlerReference encodedCatchHandlerReference;
|
|
|
|
public TryItem(EncodedCatchHandlerList encodedCatchHandlerList) {
|
|
super("try_item");
|
|
fields = new Field[] {
|
|
startAddr = new IntegerField("start_addr"),
|
|
insnCount = new ShortIntegerField("insn_count"),
|
|
encodedCatchHandlerReference = new EncodedCatchHandlerReference(encodedCatchHandlerList)
|
|
};
|
|
}
|
|
|
|
public TryItem(int startAddr, int insnCount, EncodedCatchHandler encodedCatchHandler) {
|
|
super("try_item");
|
|
fields = new Field[] {
|
|
this.startAddr = new IntegerField(startAddr, "start_addr"),
|
|
this.insnCount = new ShortIntegerField(insnCount, "insn_count"),
|
|
this.encodedCatchHandlerReference = new EncodedCatchHandlerReference(encodedCatchHandler)
|
|
};
|
|
}
|
|
|
|
public int getStartAddress() {
|
|
return startAddr.getCachedValue();
|
|
}
|
|
|
|
public int getEndAddress() {
|
|
return startAddr.getCachedValue() + insnCount.getCachedValue();
|
|
}
|
|
|
|
public EncodedCatchHandler getHandler() {
|
|
return encodedCatchHandlerReference.getReference();
|
|
}
|
|
}
|
|
|
|
public static class EncodedCatchHandlerReference extends ShortIntegerField {
|
|
private final EncodedCatchHandlerList encodedCatchHandlerList;
|
|
private EncodedCatchHandler encodedCatchHandler;
|
|
|
|
public EncodedCatchHandlerReference(EncodedCatchHandlerList encodedCatchHandlerList) {
|
|
super("encoded_catch_handler");
|
|
this.encodedCatchHandlerList = encodedCatchHandlerList;
|
|
}
|
|
|
|
public EncodedCatchHandlerReference(EncodedCatchHandler encodedCatchHandler) {
|
|
super("encoded_catch_handler");
|
|
this.encodedCatchHandlerList = null;
|
|
this.encodedCatchHandler = encodedCatchHandler;
|
|
}
|
|
|
|
public EncodedCatchHandlerList getEncodedCatchHandlerList() {
|
|
return encodedCatchHandlerList;
|
|
}
|
|
|
|
private void setReference(EncodedCatchHandler encodedCatchHandler) {
|
|
this.encodedCatchHandler = encodedCatchHandler;
|
|
}
|
|
|
|
public EncodedCatchHandler getReference() {
|
|
return encodedCatchHandler;
|
|
}
|
|
|
|
public void copyTo(DexFile dexFile, CachedIntegerValueField _copy) {
|
|
EncodedCatchHandlerReference copy = (EncodedCatchHandlerReference)_copy;
|
|
EncodedCatchHandler copiedItem = copy.getEncodedCatchHandlerList().intern(encodedCatchHandler);
|
|
copy.setReference(copiedItem);
|
|
}
|
|
|
|
public void writeTo(AnnotatedOutput 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;
|
|
//this field is only valid when reading a dex file in
|
|
protected HashMap<Integer, EncodedCatchHandler> itemsByOffset =
|
|
new HashMap<Integer, EncodedCatchHandler>();
|
|
|
|
protected HashMap<EncodedCatchHandler, EncodedCatchHandler> uniqueItems = null;
|
|
|
|
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 EncodedCatchHandler intern(EncodedCatchHandler item) {
|
|
if (uniqueItems == null) {
|
|
buildInternedItemMap();
|
|
}
|
|
EncodedCatchHandler encodedCatchHandler = uniqueItems.get(item);
|
|
if (encodedCatchHandler == null) {
|
|
encodedCatchHandler = new EncodedCatchHandler(dexFile, -1);
|
|
catchHandlerList.add(encodedCatchHandler);
|
|
item.copyTo(dexFile, encodedCatchHandler);
|
|
uniqueItems.put(encodedCatchHandler, encodedCatchHandler);
|
|
}
|
|
return encodedCatchHandler;
|
|
}
|
|
|
|
private void buildInternedItemMap() {
|
|
uniqueItems = new HashMap<EncodedCatchHandler, EncodedCatchHandler>();
|
|
for (EncodedCatchHandler item: catchHandlerList) {
|
|
uniqueItems.put(item, item);
|
|
}
|
|
}
|
|
|
|
public EncodedCatchHandlerList(final DexFile dexFile) {
|
|
super("encoded_catch_handler_list");
|
|
this.dexFile = dexFile;
|
|
|
|
fields = new Field[] {
|
|
sizeField = new ListSizeField(catchHandlerList, new Leb128Field("size")),
|
|
listField = new FieldListField<EncodedCatchHandler>(catchHandlerList, "encoded_catch_handler") {
|
|
protected EncodedCatchHandler make() {
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
private final ListSizeField sizeField;
|
|
private final FieldListField<EncodedCatchHandler> listField;
|
|
|
|
public void readFrom(Input in) {
|
|
if (tryItems.size() > 0) {
|
|
fieldPresent = true;
|
|
super.readFrom(in);
|
|
}
|
|
}
|
|
|
|
public void writeTo(AnnotatedOutput out) {
|
|
if (fieldPresent) {
|
|
super.writeTo(out);
|
|
}
|
|
}
|
|
|
|
public int place(int offset) {
|
|
for (EncodedCatchHandler encodedCatchHandler: listField.list) {
|
|
encodedCatchHandler.setBaseOffset(offset);
|
|
}
|
|
if (tryItems.size() > 0) {
|
|
fieldPresent = true;
|
|
return super.place(offset);
|
|
} else {
|
|
return offset;
|
|
}
|
|
}
|
|
|
|
public void copyTo(DexFile dexFile, EncodedCatchHandlerList copy) {
|
|
super.copyTo(dexFile, copy);
|
|
copy.fieldPresent = fieldPresent;
|
|
copy.itemsByOffset.clear();
|
|
int offset = 0;
|
|
for (EncodedCatchHandler encodedCatchHandler: copy.listField.list) {
|
|
copy.itemsByOffset.put(encodedCatchHandler.offset, encodedCatchHandler);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static class EncodedCatchHandler extends CompositeField<EncodedCatchHandler> {
|
|
private ArrayList<EncodedTypeAddrPair> list;
|
|
boolean hasCatchAll = false;
|
|
private int baseOffset = 0;
|
|
|
|
private final ListSizeField size;
|
|
private final FieldListField<EncodedTypeAddrPair> handlers;
|
|
private final Leb128Field catchAllAddress;
|
|
|
|
private int offset;
|
|
|
|
public EncodedCatchHandler(final DexFile dexFile, int offset) {
|
|
super("encoded_catch_handler");
|
|
this.offset = offset;
|
|
|
|
list = new ArrayList<EncodedTypeAddrPair>();
|
|
fields = new Field[] {
|
|
size = new ListSizeField(list, new SignedLeb128Field("size") {
|
|
public void readFrom(Input in) {
|
|
super.readFrom(in);
|
|
hasCatchAll = (getCachedValue() <= 0);
|
|
}
|
|
|
|
public void cacheValue(int value) {
|
|
super.cacheValue(value * (hasCatchAll?-1:1));
|
|
}})
|
|
,
|
|
handlers = new FieldListField<EncodedTypeAddrPair>(list, "encoded_type_addr_pair") {
|
|
protected EncodedTypeAddrPair make() {
|
|
return new EncodedTypeAddrPair(dexFile);
|
|
}
|
|
},
|
|
catchAllAddress = new Leb128Field("catch_all_addr") {
|
|
public void readFrom(Input in) {
|
|
if (hasCatchAll) {
|
|
super.readFrom(in);
|
|
}
|
|
}
|
|
|
|
public void writeTo(AnnotatedOutput out) {
|
|
if (hasCatchAll) {
|
|
super.writeTo(out);
|
|
}
|
|
}
|
|
|
|
public int place(int offset) {
|
|
if (hasCatchAll) {
|
|
return super.place(offset);
|
|
}
|
|
return offset;
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
public EncodedCatchHandler(final DexFile dexFile, List<EncodedTypeAddrPair> handlers, int catchAllHandler) {
|
|
this(dexFile, 0);
|
|
|
|
list.addAll(handlers);
|
|
if (catchAllHandler >= 0) {
|
|
hasCatchAll = true;
|
|
catchAllAddress.cacheValue(catchAllHandler);
|
|
}
|
|
}
|
|
|
|
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 int getCatchAllAddress() {
|
|
if (hasCatchAll) {
|
|
return catchAllAddress.getCachedValue();
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
public List<EncodedTypeAddrPair> getHandlers() {
|
|
return Collections.unmodifiableList(list);
|
|
}
|
|
}
|
|
|
|
public static class EncodedTypeAddrPair extends CompositeField<EncodedTypeAddrPair> {
|
|
public final IndexedItemReference<TypeIdItem> typeReferenceField;
|
|
public final Leb128Field handlerAddressField;
|
|
|
|
public EncodedTypeAddrPair(DexFile dexFile) {
|
|
super("encoded_type_addr_pair");
|
|
fields = new Field[] {
|
|
typeReferenceField = new IndexedItemReference<TypeIdItem>(dexFile.TypeIdsSection,
|
|
new Leb128Field(null), "type_idx"),
|
|
handlerAddressField = new Leb128Field("addr")
|
|
};
|
|
}
|
|
|
|
public EncodedTypeAddrPair(DexFile dexFile, TypeIdItem type, int handlerOffset) {
|
|
this(dexFile);
|
|
typeReferenceField.setReference(type);
|
|
handlerAddressField.cacheValue(handlerOffset);
|
|
}
|
|
|
|
public TypeIdItem getTypeReferenceField() {
|
|
return typeReferenceField.getReference();
|
|
}
|
|
|
|
public int getHandlerAddress() {
|
|
return handlerAddressField.getCachedValue();
|
|
}
|
|
}
|
|
|
|
private class InstructionListField implements Field<InstructionListField> {
|
|
private final DexFile dexFile;
|
|
|
|
public InstructionListField(DexFile dexFile) {
|
|
this.dexFile = dexFile;
|
|
}
|
|
|
|
public void writeTo(AnnotatedOutput out) {
|
|
int startPosition = out.getCursor();
|
|
for (InstructionField instruction: instructionList) {
|
|
instruction.writeTo(out);
|
|
}
|
|
if ((out.getCursor() - startPosition) != (instructionsSizeField.getCachedValue() * 2)) {
|
|
throw new RuntimeException("Did not write the expected amount of bytes");
|
|
}
|
|
}
|
|
|
|
public void readFrom(Input in) {
|
|
int numBytes = instructionsSizeField.getCachedValue() * 2;
|
|
int startPosition = in.getCursor();
|
|
|
|
do {
|
|
InstructionField instruction = new InstructionField(dexFile);
|
|
instruction.readFrom(in);
|
|
instructionList.add(instruction);
|
|
} while (in.getCursor() - startPosition < numBytes);
|
|
|
|
if (in.getCursor() - startPosition != numBytes) {
|
|
throw new RuntimeException("Read past the end of the code section");
|
|
}
|
|
}
|
|
|
|
public int place(int offset) {
|
|
return offset + (instructionsSizeField.getCachedValue() * 2);
|
|
}
|
|
|
|
public void copyTo(DexFile dexFile, InstructionListField copy) {
|
|
ArrayList<InstructionField> copyInstructionList = copy.getInstructionList();
|
|
copyInstructionList.clear();
|
|
for (InstructionField instruction: instructionList) {
|
|
InstructionField instructionCopy = new InstructionField(dexFile);
|
|
instruction.copyTo(dexFile, instructionCopy);
|
|
copyInstructionList.add(instructionCopy);
|
|
}
|
|
}
|
|
|
|
private ArrayList<InstructionField> getInstructionList() {
|
|
return instructionList;
|
|
}
|
|
|
|
//return the word size of the instruction list
|
|
public int getInstructionWordCount() {
|
|
int bytes = 0;
|
|
for (InstructionField instruction: instructionList) {
|
|
bytes += instruction.getSize(bytes);
|
|
}
|
|
return bytes/2;
|
|
}
|
|
|
|
//return the highest parameter word count of any method invokation
|
|
public int getOutArguments() {
|
|
int maxParamWordCount = 0;
|
|
for (InstructionField instruction: instructionList) {
|
|
IndexedItem item = instruction.getInstruction().getReferencedItem();
|
|
if (item instanceof MethodIdItem) {
|
|
MethodIdItem methodIdItem = (MethodIdItem)item;
|
|
Opcode opcode = instruction.getInstruction().getOpcode();
|
|
|
|
boolean isStatic = false;
|
|
if (opcode == Opcode.INVOKE_STATIC || opcode == Opcode.INVOKE_STATIC_RANGE) {
|
|
isStatic = true;
|
|
}
|
|
int paramWordCount = methodIdItem.getParameterRegisterCount(isStatic);
|
|
|
|
if (maxParamWordCount < paramWordCount) {
|
|
maxParamWordCount = paramWordCount;
|
|
}
|
|
}
|
|
}
|
|
return maxParamWordCount;
|
|
}
|
|
}
|
|
|
|
private class PaddingField implements Field {
|
|
|
|
public PaddingField() {
|
|
}
|
|
|
|
private boolean needsAlign() {
|
|
return (triesCountField.getCachedValue() > 0) && (instructionsSizeField.getCachedValue() % 2 == 1);
|
|
}
|
|
|
|
public void writeTo(AnnotatedOutput out) {
|
|
if (needsAlign()) {
|
|
out.writeShort(0);
|
|
}
|
|
}
|
|
|
|
public void readFrom(Input in) {
|
|
if (needsAlign()) {
|
|
in.skipBytes(2);
|
|
}
|
|
}
|
|
|
|
public int place(int offset) {
|
|
if (needsAlign()) {
|
|
return offset + 2;
|
|
} else {
|
|
return offset;
|
|
}
|
|
}
|
|
|
|
public int hashCode() {
|
|
return 0;
|
|
}
|
|
|
|
public boolean equals(Object o) {
|
|
return getClass() == o.getClass();
|
|
}
|
|
|
|
public void copyTo(DexFile dexFile, Field field) {
|
|
}
|
|
}
|
|
}
|