Refactor instructions to store individual pieces of data instead of just using an offset into a buffer

Also included is a partial implementation of the logic needed to automatically fix various types of issues

git-svn-id: https://smali.googlecode.com/svn/trunk@502 55b6fa8a-2a1e-11de-a435-ffa8d773f76a
This commit is contained in:
JesusFreke@JesusFreke.com 2009-12-23 05:26:59 +00:00
parent 9ab2b45ec8
commit fda2e631ac
59 changed files with 1810 additions and 681 deletions

View File

@ -32,6 +32,7 @@ import org.jf.dexlib.EncodedValue.EncodedValue;
import org.jf.dexlib.*;
import org.jf.dexlib.Code.InstructionIterator;
import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.Code.Format.Format;
import org.jf.dexlib.Code.Format.Instruction21c;
import org.jf.dexlib.Util.AccessFlags;
@ -119,39 +120,21 @@ public class ClassDefinition {
for (ClassDataItem.EncodedMethod directMethod: classDataItem.getDirectMethods()) {
if (directMethod.method.getMethodName().getStringValue().equals("<clinit>")) {
final byte[] encodedInstructions = directMethod.codeItem.getEncodedInstructions();
InstructionIterator.IterateInstructions(encodedInstructions,
new InstructionIterator.ProcessRawInstructionDelegate() {
public void ProcessNormalInstruction(Opcode opcode, int index) {
}
public void ProcessReferenceInstruction(Opcode opcode, int index) {
switch (opcode) {
case SPUT:
case SPUT_BOOLEAN:
case SPUT_BYTE:
case SPUT_CHAR:
case SPUT_OBJECT:
case SPUT_SHORT:
case SPUT_WIDE:
Instruction21c ins = (Instruction21c)Format.Format21c.Factory.makeInstruction(
classDefItem.getDexFile(), opcode, encodedInstructions, index);
FieldIdItem fieldIdItem = (FieldIdItem)ins.getReferencedItem();
fieldsSetInStaticConstructor.put(fieldIdItem.getIndex(), fieldIdItem);
}
}
public void ProcessPackedSwitchInstruction(int index, int targetCount, int instructionLength) {
}
public void ProcessSparseSwitchInstruction(int index, int targetCount, int instructionLength) {
}
public void ProcessFillArrayDataInstruction(int index, int elementWidth, int elementCount, int instructionLength) {
}
});
for (Instruction instruction: directMethod.codeItem.getInstructions()) {
switch (instruction.opcode) {
case SPUT:
case SPUT_BOOLEAN:
case SPUT_BYTE:
case SPUT_CHAR:
case SPUT_OBJECT:
case SPUT_SHORT:
case SPUT_WIDE:
Instruction21c ins = (Instruction21c)instruction;
FieldIdItem fieldIdItem = (FieldIdItem)ins.getReferencedItem();
fieldsSetInStaticConstructor.put(fieldIdItem.getIndex(), fieldIdItem);
}
}
}
}
}

View File

@ -43,7 +43,7 @@ public class Instruction31tMethodItem extends OffsetInstructionFormatMethodItem<
protected void setAttributes(StringTemplate template) {
super.setAttributes(template);
template.setAttribute("Register", formatRegister(instruction.getRegister()));
template.setAttribute("Register", formatRegister(instruction.getRegisterA()));
}
protected String getLabelPrefix() {

View File

@ -48,7 +48,7 @@ public class PackedSwitchMethodItem extends InstructionFormatMethodItem<PackedSw
int baseAddress) {
super(codeItem, offset, stg, instruction);
Iterator<PackedSwitchDataPseudoInstruction.PackedSwitchTarget> iterator = instruction.getTargets();
Iterator<PackedSwitchDataPseudoInstruction.PackedSwitchTarget> iterator = instruction.iterateKeysAndTargets();
while (iterator.hasNext()) {
PackedSwitchDataPseudoInstruction.PackedSwitchTarget target = iterator.next();
LabelMethodItem label = new LabelMethodItem(baseAddress + target.target, stg, "pswitch_");

View File

@ -48,11 +48,12 @@ public class SparseSwitchMethodItem extends InstructionFormatMethodItem<SparseSw
int baseAddress) {
super(codeItem, offset, stg, instruction);
Iterator<SparseSwitchDataPseudoInstruction.SparseSwitchTarget> iterator = instruction.getTargets();
Iterator<SparseSwitchDataPseudoInstruction.SparseSwitchTarget> iterator = instruction.iterateKeysAndTargets();
while (iterator.hasNext()) {
SparseSwitchDataPseudoInstruction.SparseSwitchTarget target = iterator.next();
SparseSwitchTarget sparseSwitchTarget = new SparseSwitchTarget();
sparseSwitchTarget.Value = target.value;
sparseSwitchTarget.Key = target.key;
LabelMethodItem label = new LabelMethodItem(baseAddress + target.target, stg, "sswitch_");
label = labelCache.internLabel(label);
@ -85,7 +86,7 @@ public class SparseSwitchMethodItem extends InstructionFormatMethodItem<SparseSw
}
private static class SparseSwitchTarget {
public int Value;
public int Key;
public LabelMethodItem Target;
}
}

View File

@ -36,6 +36,7 @@ import org.jf.dexlib.Code.Format.*;
import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.Code.InstructionIterator;
import org.jf.dexlib.Code.OffsetInstruction;
import org.jf.dexlib.Util.AccessFlags;
import org.antlr.stringtemplate.StringTemplateGroup;
import org.antlr.stringtemplate.StringTemplate;
@ -214,7 +215,7 @@ public class MethodDefinition {
sparseSwitchMap.put(offset + ins.getOffset(), offset);
}
offset += instruction.getSize()/2;
offset += instruction.getSize(offset*2)/2;
}
offset = 0;
@ -222,7 +223,7 @@ public class MethodDefinition {
addMethodItemsForInstruction(offset, instruction, false);
blanks.add(new BlankMethodItem(stg, offset));
offset += instruction.getSize()/2;
offset += instruction.getSize(offset*2)/2;
}
/*
@ -242,44 +243,27 @@ public class MethodDefinition {
}
}
} else {
final byte[] encodedInstructions = codeItem.getEncodedInstructions();
int currentCodeOffset = 0;
for (Instruction instruction: codeItem.getInstructions()) {
if (instruction.opcode == Opcode.PACKED_SWITCH) {
OffsetInstruction offsetInstruction = (OffsetInstruction)instruction;
packedSwitchMap.put(currentCodeOffset/2 + offsetInstruction.getOffset(), currentCodeOffset/2);
} else if (instruction.opcode == Opcode.SPARSE_SWITCH) {
OffsetInstruction offsetInstruction = (OffsetInstruction)instruction;
sparseSwitchMap.put(currentCodeOffset/2 + offsetInstruction.getOffset(), currentCodeOffset/2);
}
InstructionIterator.IterateInstructions(encodedInstructions,
new InstructionIterator.ProcessRawInstructionDelegate() {
public void ProcessNormalInstruction(Opcode opcode, int index) {
if (opcode == Opcode.PACKED_SWITCH) {
Instruction31t ins = (Instruction31t)opcode.format.Factory.makeInstruction(
dexFile, opcode, encodedInstructions, index);
packedSwitchMap.put(index/2 + ins.getOffset(), index/2);
} else if (opcode == Opcode.SPARSE_SWITCH) {
Instruction31t ins = (Instruction31t)opcode.format.Factory.makeInstruction(
dexFile, opcode, encodedInstructions, index);
sparseSwitchMap.put(index/2 + ins.getOffset(), index/2);
}
}
currentCodeOffset += instruction.getSize(currentCodeOffset);
}
public void ProcessReferenceInstruction(Opcode opcode, int index) {
}
currentCodeOffset = 0;
for (Instruction instruction: codeItem.getInstructions()) {
int offset = currentCodeOffset/2;
addMethodItemsForInstruction(offset, instruction, false);
blanks.add(new BlankMethodItem(stg, offset));
currentCodeOffset += instruction.getSize(currentCodeOffset);
}
public void ProcessPackedSwitchInstruction(int index, int targetCount, int instructionLength) {
}
public void ProcessSparseSwitchInstruction(int index, int targetCount, int instructionLength) {
}
public void ProcessFillArrayDataInstruction(int index, int elementWidth, int elementCount,
int instructionLength) {
}
});
InstructionIterator.IterateInstructions(dexFile, encodedInstructions,
new InstructionIterator.ProcessInstructionDelegate() {
public void ProcessInstruction(int index, Instruction instruction) {
int offset = index/2;
addMethodItemsForInstruction(offset, instruction, false);
blanks.add(new BlankMethodItem(stg, offset));
}
});
}
blanks.remove(blanks.size()-1);
@ -575,7 +559,7 @@ public class MethodDefinition {
int lastInstructionOffset = instructions.get(index).getOffset();
//add the catch all handler if it exists
int catchAllAddress = tryItem.encodedCatchHandler.catchAllHandlerAddress;
int catchAllAddress = tryItem.encodedCatchHandler.getCatchAllHandlerAddress();
if (catchAllAddress != -1) {
CatchMethodItem catchMethodItem = new CatchMethodItem(labels, lastInstructionOffset, stg, null,
startAddress, endAddress, catchAllAddress);
@ -586,7 +570,7 @@ public class MethodDefinition {
for (CodeItem.EncodedTypeAddrPair handler: tryItem.encodedCatchHandler.handlers) {
//use the offset from the last covered instruction
CatchMethodItem catchMethodItem = new CatchMethodItem(labels, lastInstructionOffset, stg,
handler.exceptionType, startAddress, endAddress, handler.handlerAddress);
handler.exceptionType, startAddress, endAddress, handler.getHandlerAddress());
catches.add(catchMethodItem);
}
}

View File

@ -58,7 +58,7 @@ smaliFile(AccessFlags, ClassType, SuperType, SourceFile, Interfaces, Annotations
implement(interface) ::=
<<
.implements <interface>
@ -324,7 +324,7 @@ PackedSwitchData(Opcode, FirstKey, Targets) ::=
SparseSwitchData(Opcode, Targets) ::=
<<
.sparse-switch
<Targets:{<it.Value> -> <it.Target>}; separator="\n">
<Targets:{<it.Key> -> <it.Target>}; separator="\n">
.end sparse-switch
>>
@ -366,7 +366,7 @@ RestartLocal(Register, Name, Type, Signature) ::=
SetFile(FileName) ::=
<<
.source "<FileName>"
.source "<FileName>"
>>
Blank(Blank) ::=

View File

@ -31,25 +31,53 @@ package org.jf.dexlib.Code.Format;
import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.Util.NumberUtils;
import org.jf.dexlib.Util.Output;
import org.jf.dexlib.Util.AnnotatedOutput;
import org.jf.dexlib.DexFile;
import java.util.Iterator;
public class ArrayDataPseudoInstruction extends Instruction {
public static final Instruction.InstructionFactory Factory = new Factory();
private int elementWidth;
private byte[] encodedValues;
@Override
public int getSize() {
public int getSize(int offset) {
assert offset % 2 == 0;
int size = getElementWidth() * getElementCount();
return size + (size & 0x01) + 8;
return size + (size & 0x01) + 8 + (offset % 4);
}
public static void emit(Output out, int elementWidth, byte[] encodedValues) {
public ArrayDataPseudoInstruction(int elementWidth, byte[] encodedValues) {
super(Opcode.NOP);
if (encodedValues.length % elementWidth != 0) {
throw new RuntimeException("There are not a whole number of " + elementWidth + " byte elements");
}
this.elementWidth = elementWidth;
this.encodedValues = encodedValues;
}
public ArrayDataPseudoInstruction(byte[] buffer, int bufferIndex) {
super(Opcode.NOP);
byte opcodeByte = buffer[bufferIndex];
if (opcodeByte != 0x00) {
throw new RuntimeException("Invalid opcode byte for an ArrayData pseudo-instruction");
}
byte subopcodeByte = buffer[bufferIndex+1];
if (subopcodeByte != 0x03) {
throw new RuntimeException("Invalid sub-opcode byte for an ArrayData pseudo-instruction");
}
this.elementWidth = NumberUtils.decodeUnsignedShort(buffer, bufferIndex+2);
int elementCount = NumberUtils.decodeInt(buffer, bufferIndex+4);
this.encodedValues = new byte[elementCount * elementWidth];
System.arraycopy(buffer, bufferIndex+8, encodedValues, 0, elementCount * elementWidth);
}
protected void writeInstruction(AnnotatedOutput out, int currentCodeOffset) {
//write out padding, if necessary
if (out.getCursor() % 4 != 0) {
out.writeShort(0);
@ -68,18 +96,9 @@ public class ArrayDataPseudoInstruction extends Instruction {
}
}
public ArrayDataPseudoInstruction(byte[] buffer, int bufferIndex) {
super(Opcode.NOP, buffer, bufferIndex);
byte opcodeByte = buffer[bufferIndex++];
if (opcodeByte != 0x00) {
throw new RuntimeException("Invalid opcode byte for an ArrayData pseudo-instruction");
}
byte subopcodeByte = buffer[bufferIndex];
if (subopcodeByte != 0x03) {
throw new RuntimeException("Invalid sub-opcode byte for an ArrayData pseudo-instruction");
}
protected void annotateInstruction(AnnotatedOutput out, int currentCodeOffset) {
out.annotate(getSize(currentCodeOffset), "[0x" + Integer.toHexString(currentCodeOffset/2) + "] " +
"fill-array-data instruction");
}
public Format getFormat() {
@ -87,11 +106,11 @@ public class ArrayDataPseudoInstruction extends Instruction {
}
public int getElementWidth() {
return NumberUtils.decodeUnsignedShort(buffer[bufferIndex+2], buffer[bufferIndex+3]);
return elementWidth;
}
public int getElementCount() {
return NumberUtils.decodeInt(buffer, bufferIndex+4);
return encodedValues.length / elementWidth;
}
public static class ArrayElement {
@ -108,8 +127,8 @@ public class ArrayDataPseudoInstruction extends Instruction {
return new Iterator<ArrayElement>() {
final int elementCount = getElementCount();
int i=0;
int position = bufferIndex + 8;
final ArrayElement arrayElement = new ArrayElement(buffer, getElementWidth());
int position=0;
final ArrayElement arrayElement = new ArrayElement(encodedValues, getElementWidth());
public boolean hasNext() {
return i<elementCount;

View File

@ -29,6 +29,7 @@
package org.jf.dexlib.Code.Format;
import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.Util.AnnotatedOutput;
public class DeadInstruction extends Instruction {
public final Instruction OriginalInstruction;
@ -38,9 +39,13 @@ public class DeadInstruction extends Instruction {
this.OriginalInstruction = originalInstruction;
}
protected void writeInstruction(AnnotatedOutput out, int currentCodeOffset) {
//don't write anything
}
@Override
public int getSize() {
return OriginalInstruction.getSize();
public int getSize(int offset) {
return OriginalInstruction.getSize(offset);
}
public Format getFormat() {

View File

@ -32,26 +32,48 @@ import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.Code.OffsetInstruction;
import org.jf.dexlib.DexFile;
import org.jf.dexlib.Util.Output;
import org.jf.dexlib.Util.AnnotatedOutput;
public class Instruction10t extends Instruction implements OffsetInstruction {
public static final InstructionFactory Factory = new Factory();
private int offset;
public static void emit(Output out, Opcode opcode, byte offA) {
if (offA == 0) {
public Instruction10t(Opcode opcode, int offA) {
super(opcode);
this.offset = offA;
if (offset == 0) {
throw new RuntimeException("The offset cannot be 0. Use goto/32 instead.");
}
out.writeByte(opcode.value);
out.writeByte(offA);
//allow out of range offsets here, so we have the option of replacing this instruction
//with goto/16 or goto/32 later
}
private Instruction10t(Opcode opcode, byte[] buffer, int bufferIndex) {
super(opcode, buffer, bufferIndex);
super(opcode);
if (getOffset() == 0) {
throw new RuntimeException("The offset cannot be 0. Use goto/32 instead.");
assert buffer[bufferIndex] == opcode.value;
this.offset = buffer[bufferIndex + 1];
assert offset != 0;
}
protected void writeInstruction(AnnotatedOutput out, int currentCodeOffset) {
if (offset == 0) {
throw new RuntimeException("The offset cannot be 0. Use goto/32 instead");
}
if (offset < -128 || offset > 127) {
throw new RuntimeException("The offset is out of range. It must be in [-128,-1] or [1, 127]");
}
out.writeByte(opcode.value);
out.writeByte(offset);
}
public void updateOffset(int offset) {
this.offset = offset;
}
public Format getFormat() {
@ -59,7 +81,7 @@ public class Instruction10t extends Instruction implements OffsetInstruction {
}
public int getOffset() {
return buffer[bufferIndex + 1];
return offset;
}
private static class Factory implements InstructionFactory {

View File

@ -31,22 +31,25 @@ package org.jf.dexlib.Code.Format;
import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.DexFile;
import org.jf.dexlib.Util.Output;
import org.jf.dexlib.Util.AnnotatedOutput;
public class Instruction10x extends Instruction {
public static final InstructionFactory Factory = new Factory();
public static void emit(Output out, Opcode opcode) {
out.writeByte(opcode.value);
out.writeByte(0);
public Instruction10x(Opcode opcode) {
super(opcode);
}
public Instruction10x(Opcode opcode, byte[] buffer, int bufferIndex) {
super(opcode, buffer, bufferIndex);
super(opcode);
if (buffer[bufferIndex + 1] != 0x00) {
throw new RuntimeException("The second byte of the instruction must be 0");
}
assert buffer[bufferIndex] == opcode.value;
assert buffer[bufferIndex + 1] == 0x00;
}
public void writeInstruction(AnnotatedOutput out, int currentCodeOffset) {
out.writeByte(opcode.value);
out.writeByte(0);
}
public Format getFormat() {

View File

@ -34,12 +34,16 @@ import org.jf.dexlib.Code.SingleRegisterInstruction;
import org.jf.dexlib.Code.LiteralInstruction;
import org.jf.dexlib.DexFile;
import org.jf.dexlib.Util.NumberUtils;
import org.jf.dexlib.Util.Output;
import org.jf.dexlib.Util.AnnotatedOutput;
public class Instruction11n extends Instruction implements SingleRegisterInstruction, LiteralInstruction {
public static final InstructionFactory Factory = new Factory();
private byte regA;
private byte litB;
public Instruction11n(Opcode opcode, byte regA, byte litB) {
super(opcode);
public static void emit(Output out, Opcode opcode, byte regA, byte litB) {
if (regA >= 1 << 4) {
throw new RuntimeException("The register number must be less than v16");
}
@ -49,12 +53,20 @@ public class Instruction11n extends Instruction implements SingleRegisterInstruc
throw new RuntimeException("The literal value must be between -8 and 7 inclusive");
}
out.writeByte(opcode.value);
out.writeByte((litB << 4) | regA);
this.regA = regA;
this.litB = litB;
}
private Instruction11n(Opcode opcode, byte[] buffer, int bufferIndex) {
super(opcode, buffer, bufferIndex);
super(opcode);
this.regA = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]);
this.litB = NumberUtils.decodeHighSignedNibble(buffer[bufferIndex + 1]);
}
public void writeInstruction(AnnotatedOutput out, int currentCodeOffset) {
out.writeByte(opcode.value);
out.writeByte((litB << 4) | regA);
}
public Format getFormat() {
@ -62,11 +74,11 @@ public class Instruction11n extends Instruction implements SingleRegisterInstruc
}
public int getRegisterA() {
return NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]);
return regA;
}
public long getLiteral() {
return NumberUtils.decodeHighSignedNibble(buffer[bufferIndex + 1]);
return litB;
}
private static class Factory implements Instruction.InstructionFactory {

View File

@ -33,22 +33,31 @@ import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.Code.SingleRegisterInstruction;
import org.jf.dexlib.DexFile;
import org.jf.dexlib.Util.NumberUtils;
import org.jf.dexlib.Util.Output;
import org.jf.dexlib.Util.AnnotatedOutput;
public class Instruction11x extends Instruction implements SingleRegisterInstruction {
public static final Instruction.InstructionFactory Factory = new Factory();
private byte regA;
public Instruction11x(Opcode opcode, short regA) {
super(opcode);
public static void emit(Output out, Opcode opcode, short regA) {
if (regA >= 1 << 8) {
throw new RuntimeException("The register number must be less than v256");
}
out.writeByte(opcode.value);
out.writeByte(regA);
this.regA = (byte)regA;
}
private Instruction11x(Opcode opcode, byte[] buffer, int bufferIndex) {
super(opcode, buffer, bufferIndex);
super(opcode);
this.regA = (byte)NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]);
}
public void writeInstruction(AnnotatedOutput out, int currentCodeOffset) {
out.writeByte(opcode.value);
out.writeByte(regA);
}
public Format getFormat() {
@ -56,7 +65,7 @@ public class Instruction11x extends Instruction implements SingleRegisterInstruc
}
public int getRegisterA() {
return NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]);
return regA & 0xFF;
}
private static class Factory implements Instruction.InstructionFactory {

View File

@ -33,23 +33,34 @@ import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.Code.TwoRegisterInstruction;
import org.jf.dexlib.DexFile;
import org.jf.dexlib.Util.NumberUtils;
import org.jf.dexlib.Util.Output;
import org.jf.dexlib.Util.AnnotatedOutput;
public class Instruction12x extends Instruction implements TwoRegisterInstruction {
public static final Instruction.InstructionFactory Factory = new Factory();
private int regA;
private int regB;
public Instruction12x(Opcode opcode, byte regA, byte regB) {
super(opcode);
public static void emit(Output out, Opcode opcode, byte regA, byte regB) {
if (regA >= 1 << 4 ||
regB >= 1 << 4) {
throw new RuntimeException("The register number must be less than v16");
}
out.writeByte(opcode.value);
out.writeByte((regB << 4) | regA);
this.regA = regA;
this.regB = regB;
}
private Instruction12x(Opcode opcode, byte[] buffer, int bufferIndex) {
super(opcode, buffer, bufferIndex);
super(opcode);
this.regA = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]);
this.regB = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]);
}
public void writeInstruction(AnnotatedOutput out, int currentCodeOffset) {
out.writeByte(opcode.value);
out.writeByte((regB << 4) | regA);
}
public Format getFormat() {
@ -57,11 +68,11 @@ public class Instruction12x extends Instruction implements TwoRegisterInstructio
}
public int getRegisterA() {
return NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]);
return regA;
}
public int getRegisterB() {
return NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]);
return regB;
}
private static class Factory implements Instruction.InstructionFactory {

View File

@ -32,28 +32,50 @@ import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.Code.OffsetInstruction;
import org.jf.dexlib.DexFile;
import org.jf.dexlib.Util.AnnotatedOutput;
import org.jf.dexlib.Util.NumberUtils;
import org.jf.dexlib.Util.Output;
public class Instruction20t extends Instruction implements OffsetInstruction {
public static final Instruction.InstructionFactory Factory = new Factory();
public static final InstructionFactory Factory = new Factory();
private int offset;
public static void emit(Output out, Opcode opcode, short offA) {
if (offA == 0) {
public Instruction20t(Opcode opcode, int offA) {
super(opcode);
this.offset = offA;
if (offset == 0) {
throw new RuntimeException("The offset cannot be 0. Use goto/32 instead.");
}
out.writeByte(opcode.value);
out.writeByte(0);
out.writeShort(offA);
//allow out of range offsets here, so we have the option of replacing this instruction
//with goto/16 or goto/32 later
}
private Instruction20t(Opcode opcode, byte[] buffer, int bufferIndex) {
super(opcode, buffer, bufferIndex);
super(opcode);
if (getOffset() == 0) {
throw new RuntimeException("The offset cannot be 0. Use goto/32 instead.");
assert buffer[bufferIndex] == opcode.value;
this.offset = NumberUtils.decodeShort(buffer, bufferIndex+2);
assert offset != 0;
}
protected void writeInstruction(AnnotatedOutput out, int currentCodeOffset) {
if (offset == 0) {
throw new RuntimeException("The offset cannot be 0. Use goto/32 instead");
}
if (offset < -32768 || offset > 32767) {
throw new RuntimeException("The offset is out of range. It must be in [-32768,-1] or [1, 32768]");
}
out.writeByte(opcode.value);
out.writeByte(0x00);
out.writeShort(offset);
}
public void updateOffset(int offset) {
this.offset = offset;
}
public Format getFormat() {
@ -61,10 +83,10 @@ public class Instruction20t extends Instruction implements OffsetInstruction {
}
public int getOffset() {
return NumberUtils.decodeShort(buffer, bufferIndex + 2);
return offset;
}
private static class Factory implements Instruction.InstructionFactory {
private static class Factory implements InstructionFactory {
public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
return new Instruction20t(opcode, buffer, bufferIndex);
}

View File

@ -35,13 +35,15 @@ import org.jf.dexlib.Code.SingleRegisterInstruction;
import org.jf.dexlib.DexFile;
import org.jf.dexlib.Item;
import org.jf.dexlib.TypeIdItem;
import org.jf.dexlib.Util.NumberUtils;
import org.jf.dexlib.Util.Output;
import org.jf.dexlib.Util.AnnotatedOutput;
public class Instruction21c extends InstructionWithReference implements SingleRegisterInstruction {
public static final Instruction.InstructionFactory Factory = new Factory();
private byte regA;
public Instruction21c(Opcode opcode, short regA, Item referencedItem) {
super(opcode, referencedItem);
public static void emit(Output out, Opcode opcode, short regA, Item referencedItem) {
if (regA >= 1 << 8) {
throw new RuntimeException("The register number must be less than v256");
}
@ -53,9 +55,7 @@ public class Instruction21c extends InstructionWithReference implements SingleRe
}
}
out.writeByte(opcode.value);
out.writeByte(regA);
out.writeShort(0);
this.regA = (byte)regA;
}
private Instruction21c(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
@ -64,6 +64,14 @@ public class Instruction21c extends InstructionWithReference implements SingleRe
if (opcode == Opcode.NEW_INSTANCE && ((TypeIdItem) this.getReferencedItem()).getTypeDescriptor().charAt(0) != 'L') {
throw new RuntimeException("Only class references can be used with the new-instance opcode");
}
this.regA = buffer[bufferIndex + 1];
}
protected void writeInstruction(AnnotatedOutput out, int currentCodeOffset) {
out.writeByte(opcode.value);
out.writeByte(regA);
out.writeShort(getReferencedItem().getIndex());
}
public Format getFormat() {
@ -71,7 +79,7 @@ public class Instruction21c extends InstructionWithReference implements SingleRe
}
public int getRegisterA() {
return NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]);
return regA & 0xFF;
}
private static class Factory implements Instruction.InstructionFactory {

View File

@ -34,23 +34,35 @@ import org.jf.dexlib.Code.LiteralInstruction;
import org.jf.dexlib.Code.SingleRegisterInstruction;
import org.jf.dexlib.DexFile;
import org.jf.dexlib.Util.NumberUtils;
import org.jf.dexlib.Util.Output;
import org.jf.dexlib.Util.AnnotatedOutput;
public class Instruction21h extends Instruction implements SingleRegisterInstruction, LiteralInstruction {
public static final Instruction.InstructionFactory Factory = new Factory();
private byte regA;
private short litB;
public Instruction21h(Opcode opcode, short regA, short litB) {
super(opcode);
public static void emit(Output out, Opcode opcode, short regA, short litB) {
if (regA >= 1 << 8) {
throw new RuntimeException("The register number must be less than v256");
}
out.writeByte(opcode.value);
out.writeByte(regA);
out.writeShort(litB);
this.regA = (byte)regA;
this.litB = litB;
}
private Instruction21h(Opcode opcode, byte[] buffer, int bufferIndex) {
super(opcode, buffer, bufferIndex);
super(opcode);
this.regA = buffer[bufferIndex + 1];
this.litB = NumberUtils.decodeShort(buffer, bufferIndex + 2);
}
protected void writeInstruction(AnnotatedOutput out, int currentCodeOffset) {
out.writeByte(opcode.value);
out.writeByte(regA);
out.writeShort(litB);
}
public Format getFormat() {
@ -58,11 +70,11 @@ public class Instruction21h extends Instruction implements SingleRegisterInstruc
}
public int getRegisterA() {
return NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]);
return regA & 0xFF;
}
public long getLiteral() {
return NumberUtils.decodeShort(buffer, bufferIndex + 2);
return litB;
}
private static class Factory implements Instruction.InstructionFactory {

View File

@ -34,23 +34,35 @@ import org.jf.dexlib.Code.LiteralInstruction;
import org.jf.dexlib.Code.SingleRegisterInstruction;
import org.jf.dexlib.DexFile;
import org.jf.dexlib.Util.NumberUtils;
import org.jf.dexlib.Util.Output;
import org.jf.dexlib.Util.AnnotatedOutput;
public class Instruction21s extends Instruction implements SingleRegisterInstruction, LiteralInstruction {
public static final Instruction.InstructionFactory Factory = new Factory();
private byte regA;
private short litB;
public Instruction21s(Opcode opcode, short regA, short litB) {
super(opcode);
public static void emit(Output out, Opcode opcode, short regA, short litB) {
if (regA >= 1 << 8) {
throw new RuntimeException("The register number must be less than v256");
}
out.writeByte(opcode.value);
out.writeByte(regA);
out.writeShort(litB);
this.regA = (byte)regA;
this.litB = litB;
}
private Instruction21s(Opcode opcode, byte[] buffer, int bufferIndex) {
super(opcode, buffer, bufferIndex);
super(opcode);
this.regA = buffer[bufferIndex + 1];
this.litB = NumberUtils.decodeShort(buffer, bufferIndex + 2);
}
protected void writeInstruction(AnnotatedOutput out, int currentCodeOffset) {
out.writeByte(opcode.value);
out.writeByte(regA);
out.writeShort(litB);
}
public Format getFormat() {
@ -58,11 +70,11 @@ public class Instruction21s extends Instruction implements SingleRegisterInstruc
}
public int getRegisterA() {
return NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]);
return regA & 0xFF;
}
public long getLiteral() {
return NumberUtils.decodeShort(buffer, bufferIndex + 2);
return litB;
}
private static class Factory implements Instruction.InstructionFactory {

View File

@ -33,12 +33,16 @@ import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.Code.OffsetInstruction;
import org.jf.dexlib.DexFile;
import org.jf.dexlib.Util.NumberUtils;
import org.jf.dexlib.Util.Output;
import org.jf.dexlib.Util.AnnotatedOutput;
public class Instruction21t extends Instruction implements OffsetInstruction {
public static final Instruction.InstructionFactory Factory = new Factory();
private byte regA;
private int offset;
public Instruction21t(Opcode opcode, short regA, short offB) {
super(opcode);
public static void emit(Output out, Opcode opcode, short regA, short offB) {
if (regA >= 1 << 8) {
throw new RuntimeException("The register number must be less than v256");
}
@ -47,29 +51,44 @@ public class Instruction21t extends Instruction implements OffsetInstruction {
throw new RuntimeException("The offset cannot be 0.");
}
out.writeByte(opcode.value);
out.writeByte(regA);
out.writeShort(offB);
this.regA = (byte)regA;
this.offset = offB;
}
private Instruction21t(Opcode opcode, byte[] buffer, int bufferIndex) {
super(opcode, buffer, bufferIndex);
super(opcode);
if (getOffset() == 0) {
throw new RuntimeException("The offset cannot be 0.");
assert buffer[bufferIndex] == opcode.value;
regA = buffer[bufferIndex + 1];
offset = NumberUtils.decodeShort(buffer, bufferIndex + 2);
assert offset != 0;
}
protected void writeInstruction(AnnotatedOutput out, int currentCodeOffset) {
if (offset < -32768 || offset > 32767) {
throw new RuntimeException("The offset " + offset + " is out of range. It must be in [-32768, 32767]");
}
out.writeByte(opcode.value);
out.writeByte(regA);
out.writeShort(offset);
}
public void updateOffset(int offset) {
this.offset = offset;
}
public Format getFormat() {
return Format.Format21t;
}
public short getRegister() {
return NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]);
public int getRegister() {
return regA & 0xFF;
}
public int getOffset() {
return NumberUtils.decodeShort(buffer, bufferIndex + 2);
return offset;
}
private static class Factory implements Instruction.InstructionFactory {

View File

@ -32,42 +32,56 @@ import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.Code.TwoRegisterInstruction;
import org.jf.dexlib.DexFile;
import org.jf.dexlib.Util.NumberUtils;
import org.jf.dexlib.Util.Output;
import org.jf.dexlib.Util.AnnotatedOutput;
public class Instruction22b extends Instruction implements TwoRegisterInstruction {
public static final Instruction.InstructionFactory Factory = new Factory();
private byte regA;
private byte regB;
private byte litC;
public Instruction22b(Opcode opcode, short regA, short regB, byte litC) {
super(opcode);
public static void emit(Output out, Opcode opcode, short regA, short regB, byte litC) {
if (regA >= 1 << 8 ||
regB >= 1 << 8) {
throw new RuntimeException("The register number must be less than v256");
}
this.regA = (byte)regA;
this.regB = (byte)regB;
this.litC = litC;
}
private Instruction22b(Opcode opcode, byte[] buffer, int bufferIndex) {
super(opcode);
this.regA = buffer[bufferIndex + 1];
this.regB = buffer[bufferIndex + 2];
this.litC = buffer[bufferIndex + 3];
}
protected void writeInstruction(AnnotatedOutput out, int currentCodeOffset) {
out.writeByte(opcode.value);
out.writeByte(regA);
out.writeByte(regB);
out.writeByte(litC);
}
private Instruction22b(Opcode opcode, byte[] buffer, int bufferIndex) {
super(opcode, buffer, bufferIndex);
}
public Format getFormat() {
return Format.Format22b;
}
public int getRegisterA() {
return NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]);
return regA & 0xFF;
}
public int getRegisterB() {
return NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 2]);
return regB & 0xFF;
}
public byte getLiteral() {
return buffer[bufferIndex + 3];
return litC;
}
private static class Factory implements Instruction.InstructionFactory {

View File

@ -33,25 +33,38 @@ import org.jf.dexlib.Code.InstructionWithReference;
import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.Code.TwoRegisterInstruction;
import org.jf.dexlib.DexFile;
import org.jf.dexlib.Item;
import org.jf.dexlib.Util.NumberUtils;
import org.jf.dexlib.Util.Output;
import org.jf.dexlib.Util.AnnotatedOutput;
public class Instruction22c extends InstructionWithReference implements TwoRegisterInstruction {
public static final Instruction.InstructionFactory Factory = new Factory();
private byte regA;
private byte regB;
public Instruction22c(Opcode opcode, byte regA, byte regB, Item referencedItem) {
super(opcode, referencedItem);
public static void emit(Output out, Opcode opcode, byte regA, byte regB) {
if (regA >= 1 << 4 ||
regB >= 1 << 4) {
throw new RuntimeException("The register number must be less than v16");
}
out.writeByte(opcode.value);
out.writeByte((regB << 4) | regA);
out.writeShort(0);
this.regA = regA;
this.regB = regB;
}
private Instruction22c(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
super(dexFile, opcode, buffer, bufferIndex);
this.regA = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]);
this.regB = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]);
}
protected void writeInstruction(AnnotatedOutput out, int currentCodeOffset) {
out.writeByte(opcode.value);
out.writeByte((regB << 4) | regA);
out.writeShort(getReferencedItem().getIndex());
}
public Format getFormat() {
@ -59,11 +72,11 @@ public class Instruction22c extends InstructionWithReference implements TwoRegis
}
public int getRegisterA() {
return NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]);
return regA;
}
public int getRegisterB() {
return NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]);
return regB;
}
private static class Factory implements Instruction.InstructionFactory {

View File

@ -31,14 +31,19 @@ package org.jf.dexlib.Code.Format;
import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.Code.TwoRegisterInstruction;
import org.jf.dexlib.Util.Output;
import org.jf.dexlib.Util.NumberUtils;
import org.jf.dexlib.Util.AnnotatedOutput;
import org.jf.dexlib.DexFile;
public class Instruction22cs extends Instruction implements TwoRegisterInstruction {
public static final Instruction.InstructionFactory Factory = new Factory();
private byte regA;
private byte regB;
private short fieldOffset;
public Instruction22cs(Opcode opcode, byte regA, byte regB, int fieldOffset) {
super(opcode);
public static void emit(Output out, Opcode opcode, byte regA, byte regB, int fieldOffset) {
if (regA >= 1 << 4 ||
regB >= 1 << 4) {
throw new RuntimeException("The register number must be less than v16");
@ -48,13 +53,22 @@ public class Instruction22cs extends Instruction implements TwoRegisterInstructi
throw new RuntimeException("The field offset must be less than 65536");
}
out.writeByte(opcode.value);
out.writeByte((regB << 4) | regA);
out.writeShort(fieldOffset);
this.regA = regA;
this.regB = regB;
this.fieldOffset = (short)fieldOffset;
}
private Instruction22cs(Opcode opcode, byte[] buffer, int bufferIndex) {
super(opcode, buffer, bufferIndex);
super(opcode);
this.regA = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]);
this.regB = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]);
}
protected void writeInstruction(AnnotatedOutput out, int currentCodeOffset) {
out.writeByte(opcode.value);
out.writeByte((regB << 4) | regA);
out.writeShort(fieldOffset);
}
public Format getFormat() {
@ -62,15 +76,15 @@ public class Instruction22cs extends Instruction implements TwoRegisterInstructi
}
public int getRegisterA() {
return NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]);
return regA;
}
public int getRegisterB() {
return NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]);
return regB;
}
public int getFieldOffset() {
return NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2);
return fieldOffset & 0xFFFF;
}
private static class Factory implements Instruction.InstructionFactory {

View File

@ -32,6 +32,7 @@ import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.Code.TwoRegisterInstruction;
import org.jf.dexlib.Code.InstructionWithReference;
import org.jf.dexlib.FieldIdItem;
import org.jf.dexlib.Util.AnnotatedOutput;
public class Instruction22csf extends InstructionWithReference implements TwoRegisterInstruction {
private final Instruction22cs unfixedInstruction;
@ -42,6 +43,15 @@ public class Instruction22csf extends InstructionWithReference implements TwoReg
this.unfixedInstruction = unfixedInstruction;
}
protected void writeInstruction(AnnotatedOutput out, int currentCodeOffset) {
byte regA = (byte)getRegisterA();
byte regB = (byte)getRegisterB();
out.writeByte(opcode.value);
out.writeByte((regB << 4) | regA);
out.writeShort(this.getReferencedItem().getIndex());
}
public Format getFormat() {
return Format.Format22csf;
}

View File

@ -33,24 +33,39 @@ import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.Code.TwoRegisterInstruction;
import org.jf.dexlib.DexFile;
import org.jf.dexlib.Util.NumberUtils;
import org.jf.dexlib.Util.Output;
import org.jf.dexlib.Util.AnnotatedOutput;
public class Instruction22s extends Instruction implements TwoRegisterInstruction {
public static final Instruction.InstructionFactory Factory = new Factory();
private byte regA;
private byte regB;
private short litC;
public Instruction22s(Opcode opcode, byte regA, byte regB, short litC) {
super(opcode);
public static void emit(Output out, Opcode opcode, byte regA, byte regB, short litC) {
if (regA >= 1 << 4 ||
regB >= 1 << 4) {
throw new RuntimeException("The register number must be less than v16");
}
out.writeByte(opcode.value);
out.writeByte((regB << 4) | regA);
out.writeShort(litC);
this.regA = regA;
this.regB = regB;
this.litC = litC;
}
private Instruction22s(Opcode opcode, byte[] buffer, int bufferIndex) {
super(opcode, buffer, bufferIndex);
super(opcode);
this.regA = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]);
this.regB = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]);
this.litC = NumberUtils.decodeShort(buffer, bufferIndex + 2);
}
protected void writeInstruction(AnnotatedOutput out, int currentCodeOffset) {
out.writeByte(opcode.value);
out.writeByte((regB << 4) | regA);
out.writeShort(litC);
}
public Format getFormat() {
@ -58,15 +73,15 @@ public class Instruction22s extends Instruction implements TwoRegisterInstructio
}
public int getRegisterA() {
return NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]);
return regA;
}
public int getRegisterB() {
return NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]);
return regB;
}
public short getLiteral() {
return NumberUtils.decodeShort(buffer, bufferIndex + 2);
return litC;
}
private static class Factory implements Instruction.InstructionFactory {

View File

@ -31,16 +31,22 @@ package org.jf.dexlib.Code.Format;
import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.Code.OffsetInstruction;
import org.jf.dexlib.Code.TwoRegisterInstruction;
import org.jf.dexlib.DexFile;
import org.jf.dexlib.Util.NumberUtils;
import org.jf.dexlib.Util.Output;
import org.jf.dexlib.Util.AnnotatedOutput;
public class Instruction22t extends Instruction implements OffsetInstruction {
public class Instruction22t extends Instruction implements OffsetInstruction, TwoRegisterInstruction {
public static final Instruction.InstructionFactory Factory = new Factory();
private byte regA;
private byte regB;
private int offset;
public static void emit(Output out, Opcode opcode, byte regA, byte regB, short offC) {
if (regA >= 1 << 4 ||
regB >= 1 << 4) {
public Instruction22t(Opcode opcode, byte regA, byte regB, short offC) {
super(opcode);
if (regA >= 16 ||
regB >= 16) {
throw new RuntimeException("The register number must be less than v16");
}
@ -48,33 +54,51 @@ public class Instruction22t extends Instruction implements OffsetInstruction {
throw new RuntimeException("The offset cannot be 0.");
}
out.writeByte(opcode.value);
out.writeByte((regB << 4) | regA);
out.writeShort(offC);
this.regA = regA;
this.regB = regB;
this.offset = offC;
}
private Instruction22t(Opcode opcode, byte[] buffer, int bufferIndex) {
super(opcode, buffer, bufferIndex);
super(opcode);
if (getOffset() == 0) {
throw new RuntimeException("The offset cannot be 0.");
assert buffer[bufferIndex] == opcode.value;
regA = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]);
regB = NumberUtils.decodeHighSignedNibble(buffer[bufferIndex + 1]);
offset = NumberUtils.decodeShort(buffer, bufferIndex + 2);
assert offset != 0;
}
protected void writeInstruction(AnnotatedOutput out, int currentCodeOffset) {
if (offset < -32768 || offset > 32767) {
throw new RuntimeException("The offset " + offset + " is out of range. It must be in [-32768, 32767]");
}
out.writeByte(opcode.value);
out.writeByte((regB << 4) | regA);
out.writeShort(offset);
}
public void updateOffset(int offset) {
this.offset = offset;
}
public Format getFormat() {
return Format.Format22t;
}
public byte getRegisterA() {
return NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]);
public int getRegisterA() {
return regA;
}
public byte getRegisterB() {
return NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]);
public int getRegisterB() {
return regB;
}
public int getOffset() {
return NumberUtils.decodeShort(buffer, bufferIndex + 2);
return offset;
}
private static class Factory implements Instruction.InstructionFactory {
@ -82,4 +106,4 @@ public class Instruction22t extends Instruction implements OffsetInstruction {
return new Instruction22t(opcode, buffer, bufferIndex);
}
}
}
}

View File

@ -33,12 +33,16 @@ import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.Code.TwoRegisterInstruction;
import org.jf.dexlib.DexFile;
import org.jf.dexlib.Util.NumberUtils;
import org.jf.dexlib.Util.Output;
import org.jf.dexlib.Util.AnnotatedOutput;
public class Instruction22x extends Instruction implements TwoRegisterInstruction {
public static final Instruction.InstructionFactory Factory = new Factory();
private byte regA;
private short regB;
public Instruction22x(Opcode opcode, short regA, int regB) {
super(opcode);
public static void emit(Output out, Opcode opcode, short regA, int regB) {
if (regA >= 1 << 8) {
throw new RuntimeException("The register number must be less than v16");
}
@ -47,13 +51,21 @@ public class Instruction22x extends Instruction implements TwoRegisterInstructio
throw new RuntimeException("The register number must be less than v65536");
}
out.writeByte(opcode.value);
out.writeByte(regA);
out.writeShort(regB);
this.regA = (byte)regA;
this.regB = (short)regB;
}
private Instruction22x(Opcode opcode, byte[] buffer, int bufferIndex) {
super(opcode, buffer, bufferIndex);
super(opcode);
this.regA = buffer[bufferIndex + 1];
this.regB = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2);
}
protected void writeInstruction(AnnotatedOutput out, int currentCodeOffset) {
out.writeByte(opcode.value);
out.writeByte(regA);
out.writeShort(regB);
}
public Format getFormat() {
@ -61,11 +73,11 @@ public class Instruction22x extends Instruction implements TwoRegisterInstructio
}
public int getRegisterA() {
return NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]);
return regA & 0xFF;
}
public int getRegisterB() {
return NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2);
return regB & 0xFFFF;
}
private static class Factory implements Instruction.InstructionFactory {

View File

@ -32,43 +32,57 @@ import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.Code.ThreeRegisterInstruction;
import org.jf.dexlib.DexFile;
import org.jf.dexlib.Util.NumberUtils;
import org.jf.dexlib.Util.Output;
import org.jf.dexlib.Util.AnnotatedOutput;
public class Instruction23x extends Instruction implements ThreeRegisterInstruction {
public static final Instruction.InstructionFactory Factory = new Factory();
private byte regA;
private byte regB;
private byte regC;
public Instruction23x(Opcode opcode, short regA, short regB, short regC) {
super(opcode);
public static void emit(Output out, Opcode opcode, short regA, short regB, short regC) {
if (regA >= 1 << 8 ||
regB >= 1 << 8 ||
regC >= 1 << 8) {
throw new RuntimeException("The register number must be less than v256");
}
this.regA = (byte)regA;
this.regB = (byte)regB;
this.regC = (byte)regC;
}
private Instruction23x(Opcode opcode, byte[] buffer, int bufferIndex) {
super(opcode);
this.regA = buffer[bufferIndex + 1];
this.regB = buffer[bufferIndex + 2];
this.regC = buffer[bufferIndex + 3];
}
protected void writeInstruction(AnnotatedOutput out, int currentCodeOffset) {
out.writeByte(opcode.value);
out.writeByte(regA);
out.writeByte(regB);
out.writeByte(regC);
}
private Instruction23x(Opcode opcode, byte[] buffer, int bufferIndex) {
super(opcode, buffer, bufferIndex);
}
public Format getFormat() {
return Format.Format23x;
}
public int getRegisterA() {
return NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]);
return regA & 0xFF;
}
public int getRegisterB() {
return NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 2]);
return regB & 0xFF;
}
public int getRegisterC() {
return NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 3]);
return regC & 0xFF;
}
private static class Factory implements Instruction.InstructionFactory {

View File

@ -32,20 +32,34 @@ import org.jf.dexlib.DexFile;
import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.Code.OffsetInstruction;
import org.jf.dexlib.Util.AnnotatedOutput;
import org.jf.dexlib.Util.NumberUtils;
import org.jf.dexlib.Util.Output;
public class Instruction30t extends Instruction implements OffsetInstruction {
public static final Instruction.InstructionFactory Factory = new Factory();
public static final InstructionFactory Factory = new Factory();
private int offset;
public static void emit(Output out, Opcode opcode, int offA) {
out.writeByte(opcode.value);
out.writeByte(0);
out.writeInt(offA);
public Instruction30t(Opcode opcode, int offA) {
super(opcode);
this.offset = offA;
}
private Instruction30t(Opcode opcode, byte[] buffer, int bufferIndex) {
super(opcode, buffer, bufferIndex);
super(opcode);
assert buffer[bufferIndex] == opcode.value;
this.offset = NumberUtils.decodeInt(buffer, bufferIndex+2);
}
protected void writeInstruction(AnnotatedOutput out, int currentCodeOffset) {
out.writeByte(opcode.value);
out.writeByte(0x00);
out.writeInt(offset);
}
public void updateOffset(int offset) {
this.offset = offset;
}
public Format getFormat() {
@ -53,10 +67,10 @@ public class Instruction30t extends Instruction implements OffsetInstruction {
}
public int getOffset() {
return NumberUtils.decodeInt(buffer, bufferIndex + 2);
return offset;
}
private static class Factory implements Instruction.InstructionFactory {
private static class Factory implements InstructionFactory {
public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
return new Instruction30t(opcode, buffer, bufferIndex);
}

View File

@ -35,27 +35,36 @@ import org.jf.dexlib.Code.SingleRegisterInstruction;
import org.jf.dexlib.DexFile;
import org.jf.dexlib.Item;
import org.jf.dexlib.Util.NumberUtils;
import org.jf.dexlib.Util.Output;
import org.jf.dexlib.Util.AnnotatedOutput;
public class Instruction31c extends InstructionWithReference implements SingleRegisterInstruction {
public static final Instruction.InstructionFactory Factory = new Factory();
private byte regA;
public Instruction31c(Opcode opcode, short regA, Item referencedItem) {
super(opcode, referencedItem);
public static void emit(Output out, Opcode opcode, short regA) {
if (regA >= 1 << 8) {
throw new RuntimeException("The register number must be less than v256");
}
out.writeByte(opcode.value);
out.writeByte(regA);
out.writeInt(0);
this.regA = (byte)regA;
}
private Instruction31c(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
super(dexFile, opcode, buffer, bufferIndex);
this.regA = buffer[bufferIndex + 1];
}
protected int getReferencedItemIndex() {
return NumberUtils.decodeInt(buffer, bufferIndex + 2);
protected void writeInstruction(AnnotatedOutput out, int currentCodeOffset) {
out.writeByte(opcode.value);
out.writeByte(regA);
out.writeInt(getReferencedItem().getIndex());
}
protected int getReferencedItemIndex(byte[] buffer, int bufferIndex) {
return NumberUtils.decodeInt(buffer, bufferIndex + 2);
}
public Format getFormat() {
@ -63,7 +72,7 @@ public class Instruction31c extends InstructionWithReference implements SingleRe
}
public int getRegisterA() {
return NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]);
return regA & 0xFF;
}
private static class Factory implements Instruction.InstructionFactory {

View File

@ -34,23 +34,35 @@ import org.jf.dexlib.Code.LiteralInstruction;
import org.jf.dexlib.Code.SingleRegisterInstruction;
import org.jf.dexlib.DexFile;
import org.jf.dexlib.Util.NumberUtils;
import org.jf.dexlib.Util.Output;
import org.jf.dexlib.Util.AnnotatedOutput;
public class Instruction31i extends Instruction implements SingleRegisterInstruction, LiteralInstruction {
public static final Instruction.InstructionFactory Factory = new Factory();
private byte regA;
private int litB;
public Instruction31i(Opcode opcode, short regA, int litB) {
super(opcode);
public static void emit(Output out, Opcode opcode, short regA, int litB) {
if (regA >= 1 << 8) {
throw new RuntimeException("The register number must be less than v256");
}
out.writeByte(opcode.value);
out.writeByte(regA);
out.writeInt(litB);
this.regA = (byte)regA;
this.litB = litB;
}
private Instruction31i(Opcode opcode, byte[] buffer, int bufferIndex) {
super(opcode, buffer, bufferIndex);
super(opcode);
this.regA = (byte)NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]);
this.litB = NumberUtils.decodeInt(buffer, bufferIndex + 2);
}
protected void writeInstruction(AnnotatedOutput out, int currentCodeOffset) {
out.writeByte(opcode.value);
out.writeByte(regA);
out.writeInt(litB);
}
public Format getFormat() {
@ -58,11 +70,11 @@ public class Instruction31i extends Instruction implements SingleRegisterInstruc
}
public int getRegisterA() {
return NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]);
return regA & 0xFF;
}
public long getLiteral() {
return NumberUtils.decodeInt(buffer, bufferIndex + 2);
return litB;
}
private static class Factory implements Instruction.InstructionFactory {

View File

@ -31,37 +31,55 @@ package org.jf.dexlib.Code.Format;
import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.Code.OffsetInstruction;
import org.jf.dexlib.Code.SingleRegisterInstruction;
import org.jf.dexlib.DexFile;
import org.jf.dexlib.Util.NumberUtils;
import org.jf.dexlib.Util.Output;
import org.jf.dexlib.Util.AnnotatedOutput;
public class Instruction31t extends Instruction implements OffsetInstruction {
public class Instruction31t extends Instruction implements OffsetInstruction, SingleRegisterInstruction {
public static final Instruction.InstructionFactory Factory = new Factory();
private byte regA;
private int offset;
public Instruction31t(Opcode opcode, short regA, int offB) {
super(opcode);
public static void emit(Output out, Opcode opcode, short regA, int offB) {
if (regA >= 1 << 8) {
throw new RuntimeException("The register number must be less than v256");
}
out.writeByte(opcode.value);
out.writeByte(regA);
out.writeInt(offB);
this.regA = (byte)regA;
this.offset = offB;
}
private Instruction31t(Opcode opcode, byte[] buffer, int bufferIndex) {
super(opcode, buffer, bufferIndex);
super(opcode);
this.regA = buffer[bufferIndex + 1];
this.offset = NumberUtils.decodeInt(buffer, bufferIndex + 2);
}
protected void writeInstruction(AnnotatedOutput out, int currentCodeOffset) {
out.writeByte(opcode.value);
out.writeByte(regA);
//TODO: get offset from offsetTarget
out.writeInt(offset);
}
public void updateOffset(int offset) {
this.offset = offset;
}
public Format getFormat() {
return Format.Format31t;
}
public short getRegister() {
return NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]);
public int getRegisterA() {
return regA & 0xFF;
}
public int getOffset() {
return NumberUtils.decodeInt(buffer, bufferIndex + 2);
return offset;
}
private static class Factory implements Instruction.InstructionFactory {

View File

@ -33,37 +33,49 @@ import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.Code.TwoRegisterInstruction;
import org.jf.dexlib.Util.NumberUtils;
import org.jf.dexlib.Util.Output;
import org.jf.dexlib.Util.AnnotatedOutput;
public class Instruction32x extends Instruction implements TwoRegisterInstruction {
public static final Instruction.InstructionFactory Factory = new Factory();
private short regA;
private short regB;
public Instruction32x(Opcode opcode, int regA, int regB) {
super(opcode);
public static void emit(Output out, Opcode opcode, int regA, int regB) {
if (regA >= 1<<16 ||
regB >= 1<<16) {
throw new RuntimeException("The register number must be less than v65536");
}
this.regA = (short)regA;
this.regB = (short)regB;
}
private Instruction32x(Opcode opcode, byte[] buffer, int bufferIndex) {
super(opcode);
this.regA = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2);
this.regB = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 4);
}
protected void writeInstruction(AnnotatedOutput out, int currentCodeOffset) {
out.writeByte(opcode.value);
out.writeByte(0);
out.writeShort(regA);
out.writeShort(regB);
}
private Instruction32x(Opcode opcode, byte[] buffer, int bufferIndex) {
super(opcode, buffer, bufferIndex);
}
public Format getFormat() {
return Format.Format32x;
}
public int getRegisterA() {
return NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2);
return regA & 0xFFFF;
}
public int getRegisterB() {
return NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 4);
return regB & 0xFFFF;
}
private static class Factory implements Instruction.InstructionFactory {

View File

@ -37,13 +37,21 @@ import org.jf.dexlib.Item;
import org.jf.dexlib.MethodIdItem;
import org.jf.dexlib.TypeIdItem;
import org.jf.dexlib.Util.NumberUtils;
import org.jf.dexlib.Util.Output;
import org.jf.dexlib.Util.AnnotatedOutput;
public class Instruction35c extends InstructionWithReference {
public static final Instruction.InstructionFactory Factory = new Factory();
private byte regCount;
private byte regA;
private byte regD;
private byte regE;
private byte regF;
private byte regG;
public static void emit(Output out, Opcode opcode, int regCount, byte regD, byte regE, byte regF, byte regG,
public Instruction35c(Opcode opcode, int regCount, byte regD, byte regE, byte regF, byte regG,
byte regA, Item referencedItem) {
super(opcode, referencedItem);
if (regCount > 5) {
throw new RuntimeException("regCount cannot be greater than 5");
}
@ -56,11 +64,12 @@ public class Instruction35c extends InstructionWithReference {
throw new RuntimeException("All register args must fit in 4 bits");
}
out.writeByte(opcode.value);
out.writeByte((regCount << 4) | regA);
out.writeShort(0);
out.writeByte((regE << 4) | regD);
out.writeByte((regG << 4) | regF);
this.regCount = (byte)regCount;
this.regA = regA;
this.regD = regD;
this.regE = regE;
this.regF = regF;
this.regG = regG;
checkItem(opcode, referencedItem, regCount);
}
@ -72,35 +81,50 @@ public class Instruction35c extends InstructionWithReference {
throw new RuntimeException("regCount cannot be greater than 5");
}
this.regCount = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]);
this.regA = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]);
this.regD = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 4]);
this.regE = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 4]);
this.regF = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 5]);
this.regG = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 5]);
checkItem(opcode, getReferencedItem(), getRegCount());
}
protected void writeInstruction(AnnotatedOutput out, int currentCodeOffset) {
out.writeByte(opcode.value);
out.writeByte((regCount << 4) | regA);
out.writeShort(getReferencedItem().getIndex());
out.writeByte((regE << 4) | regD);
out.writeByte((regG << 4) | regF);
}
public Format getFormat() {
return Format.Format35c;
}
public byte getRegisterA() {
return NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]);
return regA;
}
public byte getRegCount() {
return NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]);
return regCount;
}
public byte getRegisterD() {
return NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 4]);
return regD;
}
public byte getRegisterE() {
return NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 4]);
return regE;
}
public byte getRegisterF() {
return NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 5]);
return regF;
}
public byte getRegisterG() {
return NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 5]);
return regG;
}
private static void checkItem(Opcode opcode, Item item, int regCount) {
@ -131,4 +155,4 @@ public class Instruction35c extends InstructionWithReference {
return new Instruction35c(dexFile, opcode, buffer, bufferIndex);
}
}
}
}

View File

@ -30,16 +30,24 @@ package org.jf.dexlib.Code.Format;
import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.Util.Output;
import org.jf.dexlib.Util.NumberUtils;
import org.jf.dexlib.Util.AnnotatedOutput;
import org.jf.dexlib.DexFile;
public class Instruction35ms extends Instruction {
public static final Instruction.InstructionFactory Factory = new Factory();
private byte regCount;
private byte regA;
private byte regD;
private byte regE;
private byte regF;
private byte regG;
private short methodIndex;
public static void emit(Output out, Opcode opcode, int regCount, byte regD, byte regE, byte regF, byte regG,
public Instruction35ms(Opcode opcode, int regCount, byte regD, byte regE, byte regF, byte regG,
byte regA, int methodIndex) {
super(opcode);
if (regCount > 5) {
throw new RuntimeException("regCount cannot be greater than 5");
}
@ -56,6 +64,28 @@ public class Instruction35ms extends Instruction {
throw new RuntimeException("The method index must be less than 65536");
}
this.regCount = (byte)regCount;
this.regA = regA;
this.regD = regD;
this.regE = regE;
this.regF = regF;
this.regG = regG;
this.methodIndex = (short)methodIndex;
}
private Instruction35ms(Opcode opcode, byte[] buffer, int bufferIndex) {
super(opcode);
this.regCount = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]);
this.regA = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]);
this.regD = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 4]);
this.regE = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 4]);
this.regF = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 5]);
this.regG = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 5]);
this.methodIndex = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2);
}
protected void writeInstruction(AnnotatedOutput out, int currentCodeOffset) {
out.writeByte(opcode.value);
out.writeByte((regCount << 4) | regA);
out.writeShort(methodIndex);
@ -63,40 +93,36 @@ public class Instruction35ms extends Instruction {
out.writeByte((regG << 4) | regF);
}
private Instruction35ms(Opcode opcode, byte[] buffer, int bufferIndex) {
super(opcode, buffer, bufferIndex);
}
public Format getFormat() {
return Format.Format35ms;
}
public byte getRegisterA() {
return NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]);
return regA;
}
public byte getRegCount() {
return NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]);
return regCount;
}
public byte getRegisterD() {
return NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 4]);
return regD;
}
public byte getRegisterE() {
return NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 4]);
return regE;
}
public byte getRegisterF() {
return NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 5]);
return regF;
}
public byte getRegisterG() {
return NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 5]);
return regG;
}
public int getMethodIndex() {
return NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2);
return methodIndex & 0xFFFF;
}
private static class Factory implements Instruction.InstructionFactory {

View File

@ -31,6 +31,7 @@ package org.jf.dexlib.Code.Format;
import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.Code.InstructionWithReference;
import org.jf.dexlib.MethodIdItem;
import org.jf.dexlib.Util.AnnotatedOutput;
public class Instruction35msf extends InstructionWithReference {
private final Instruction35ms unfixedInstruction;
@ -41,11 +42,26 @@ public class Instruction35msf extends InstructionWithReference {
this.unfixedInstruction = unfixedInstruction;
}
protected void writeInstruction(AnnotatedOutput out, int currentCodeOffset) {
byte regA = getRegisterA();
byte regCount = getRegCount();
byte regD = getRegisterD();
byte regE = getRegisterE();
byte regF = getRegisterF();
byte regG = getRegisterG();
out.writeByte(opcode.value);
out.writeByte((regCount << 4) | regA);
out.writeShort(this.getReferencedItem().getIndex());
out.writeByte((regE << 4) | regD);
out.writeByte((regG << 4) | regF);
}
public Format getFormat() {
return Format.Format35msf;
}
public int getRegisterA() {
public byte getRegisterA() {
return unfixedInstruction.getRegisterA();
}

View File

@ -30,24 +30,124 @@ package org.jf.dexlib.Code.Format;
import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.Util.Output;
import org.jf.dexlib.Code.InstructionWithReference;
import static org.jf.dexlib.Code.Opcode.*;
import org.jf.dexlib.Util.AnnotatedOutput;
import org.jf.dexlib.Util.NumberUtils;
import org.jf.dexlib.Item;
import org.jf.dexlib.DexFile;
import org.jf.dexlib.TypeIdItem;
import org.jf.dexlib.MethodIdItem;
public class Instruction35s extends Instruction35c {
public class Instruction35s extends InstructionWithReference {
public static final Instruction.InstructionFactory Factory = new Factory();
private byte regCount;
private byte regA;
private byte regD;
private byte regE;
private byte regF;
private byte regG;
public static void emit(Output out, Opcode opcode, int regCount, byte regD, byte regE, byte regF, byte regG,
public Instruction35s(Opcode opcode, int regCount, byte regD, byte regE, byte regF, byte regG,
byte regA, Item referencedItem) {
Instruction35c.emit(out, opcode, regCount, regD, regE, regF, regG, regA, referencedItem);
super(opcode, referencedItem);
if (regCount > 5) {
throw new RuntimeException("regCount cannot be greater than 5");
}
if (regD >= 1 << 4 ||
regE >= 1 << 4 ||
regF >= 1 << 4 ||
regG >= 1 << 4 ||
regA >= 1 << 4) {
throw new RuntimeException("All register args must fit in 4 bits");
}
checkItem(opcode, referencedItem, regCount);
this.regCount = (byte)regCount;
this.regA = regA;
this.regD = regD;
this.regE = regE;
this.regF = regF;
this.regG = regG;
}
private Instruction35s(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
protected Instruction35s(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
super(dexFile, opcode, buffer, bufferIndex);
if (getRegCount() > 5) {
throw new RuntimeException("regCount cannot be greater than 5");
}
checkItem(opcode, getReferencedItem(), getRegCount());
this.regCount = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 1]);
this.regA = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 1]);
this.regD = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 4]);
this.regE = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 4]);
this.regF = NumberUtils.decodeLowUnsignedNibble(buffer[bufferIndex + 5]);
this.regG = NumberUtils.decodeHighUnsignedNibble(buffer[bufferIndex + 5]);
}
protected void writeInstruction(AnnotatedOutput out, int currentCodeOffset) {
out.writeByte(opcode.value);
out.writeByte((regCount << 4) | regA);
out.writeShort(getReferencedItem().getIndex());
out.writeByte((regE << 4) | regD);
out.writeByte((regG << 4) | regF);
}
public Format getFormat() {
return Format.Format35s;
return Format.Format35c;
}
public byte getRegisterA() {
return regA;
}
public byte getRegCount() {
return regCount;
}
public byte getRegisterD() {
return regD;
}
public byte getRegisterE() {
return regE;
}
public byte getRegisterF() {
return regF;
}
public byte getRegisterG() {
return regG;
}
private static void checkItem(Opcode opcode, Item item, int regCount) {
if (opcode == FILLED_NEW_ARRAY) {
//check data for filled-new-array opcode
String type = ((TypeIdItem) item).getTypeDescriptor();
if (type.charAt(0) != '[') {
throw new RuntimeException("The type must be an array type");
}
if (type.charAt(1) == 'J' || type.charAt(1) == 'D') {
throw new RuntimeException("The type cannot be an array of longs or doubles");
}
} else if (opcode.value >= INVOKE_VIRTUAL.value && opcode.value <= INVOKE_INTERFACE.value) {
//check data for invoke-* opcodes
MethodIdItem methodIdItem = (MethodIdItem) item;
int parameterRegisterCount = methodIdItem.getPrototype().getParameterRegisterCount();
if (opcode != INVOKE_STATIC) {
parameterRegisterCount++;
}
if (parameterRegisterCount != regCount) {
throw new RuntimeException("regCount does not match the number of arguments of the method");
}
}
}
private static class Factory implements Instruction.InstructionFactory {

View File

@ -30,6 +30,7 @@ package org.jf.dexlib.Code.Format;
import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.Code.InstructionWithReference;
import org.jf.dexlib.Util.AnnotatedOutput;
public class Instruction35sf extends InstructionWithReference {
private final Instruction35s unfixedInstruction;
@ -39,6 +40,21 @@ public class Instruction35sf extends InstructionWithReference {
this.unfixedInstruction = unfixedInstruction;
}
protected void writeInstruction(AnnotatedOutput out, int currentCodeOffset) {
byte regA = getRegisterA();
byte regCount = getRegCount();
byte regD = getRegisterD();
byte regE = getRegisterE();
byte regF = getRegisterF();
byte regG = getRegisterG();
out.writeByte(opcode.value);
out.writeByte((regCount << 4) | regA);
out.writeShort(getReferencedItem().getIndex());
out.writeByte((regE << 4) | regD);
out.writeByte((regG << 4) | regF);
}
public Format getFormat() {
return Format.Format35sf;
}

View File

@ -37,12 +37,16 @@ import org.jf.dexlib.Item;
import org.jf.dexlib.MethodIdItem;
import org.jf.dexlib.TypeIdItem;
import org.jf.dexlib.Util.NumberUtils;
import org.jf.dexlib.Util.Output;
import org.jf.dexlib.Util.AnnotatedOutput;
public class Instruction3rc extends InstructionWithReference {
public static final Instruction.InstructionFactory Factory = new Factory();
private byte regCount;
private short startReg;
public Instruction3rc(Opcode opcode, short regCount, int startReg, Item referencedItem) {
super(opcode, referencedItem);
public static void emit(Output out, Opcode opcode, short regCount, int startReg, Item referencedItem) {
if (regCount >= 1 << 8) {
throw new RuntimeException("regCount must be less than 256");
}
@ -57,10 +61,8 @@ public class Instruction3rc extends InstructionWithReference {
throw new RuntimeException("The beginning register of the range cannot be negative");
}
out.writeByte(opcode.value);
out.writeByte(regCount);
out.writeShort(0);
out.writeShort(startReg);
this.regCount = (byte)regCount;
this.startReg = (short)startReg;
checkItem(opcode, referencedItem, regCount);
}
@ -68,19 +70,29 @@ public class Instruction3rc extends InstructionWithReference {
private Instruction3rc(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
super(dexFile, opcode, buffer, bufferIndex);
this.regCount = (byte)NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]);
this.startReg = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 4);
checkItem(opcode, getReferencedItem(), getRegCount());
}
protected void writeInstruction(AnnotatedOutput out, int currentCodeOffset) {
out.writeByte(opcode.value);
out.writeByte(regCount);
out.writeShort(this.getReferencedItem().getIndex());
out.writeShort(startReg);
}
public Format getFormat() {
return Format.Format3rc;
}
public short getRegCount() {
return NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]);
return (short)(regCount & 0xFF);
}
public int getStartRegister() {
return NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 4);
return startReg & 0xFFFF;
}
private static void checkItem(Opcode opcode, Item item, int regCount) {

View File

@ -30,14 +30,19 @@ package org.jf.dexlib.Code.Format;
import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.Util.Output;
import org.jf.dexlib.Util.NumberUtils;
import org.jf.dexlib.Util.AnnotatedOutput;
import org.jf.dexlib.DexFile;
public class Instruction3rms extends Instruction {
public static final Instruction.InstructionFactory Factory = new Factory();
private byte regCount;
private short startReg;
private short methodIndex;
public Instruction3rms(Opcode opcode, short regCount, int startReg, int methodIndex) {
super(opcode);
public static void emit(Output out, Opcode opcode, short regCount, int startReg, int methodIndex) {
if (regCount >= 1 << 8) {
throw new RuntimeException("regCount must be less than 256");
}
@ -56,14 +61,24 @@ public class Instruction3rms extends Instruction {
throw new RuntimeException("The method index must be less than 65536");
}
out.writeByte(opcode.value);
out.writeByte(regCount);
out.writeShort(0);
out.writeShort(startReg);
this.regCount = (byte)regCount;
this.startReg = (short)startReg;
this.methodIndex = (short)methodIndex;
}
private Instruction3rms(Opcode opcode, byte[] buffer, int bufferIndex) {
super(opcode, buffer, bufferIndex);
super(opcode);
this.regCount = (byte)NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]);
this.methodIndex = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2);
this.startReg = (short)NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 4);
}
protected void writeInstruction(AnnotatedOutput out, int currentCodeOffset) {
out.writeByte(opcode.value);
out.writeByte(regCount);
out.writeShort(methodIndex);
out.writeShort(startReg);
}
public Format getFormat() {
@ -71,16 +86,16 @@ public class Instruction3rms extends Instruction {
}
public short getRegCount() {
return NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]);
return (short)(regCount & 0xFF);
}
public int getStartRegister() {
return NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 4);
return startReg & 0xFFFF;
}
public int getMethodIndex() {
return NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2);
}
return methodIndex & 0xFFFF;
}
private static class Factory implements Instruction.InstructionFactory {
public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {

View File

@ -31,6 +31,7 @@ package org.jf.dexlib.Code.Format;
import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.Code.InstructionWithReference;
import org.jf.dexlib.MethodIdItem;
import org.jf.dexlib.Util.AnnotatedOutput;
public class Instruction3rmsf extends InstructionWithReference {
private final Instruction3rms unfixedInstruction;
@ -41,6 +42,16 @@ public class Instruction3rmsf extends InstructionWithReference {
this.unfixedInstruction = unfixedInstruction;
}
protected void writeInstruction(AnnotatedOutput out, int currentCodeOffset) {
short regCount = getRegCount();
int startReg = getStartRegister();
out.writeByte(opcode.value);
out.writeByte(regCount);
out.writeShort(this.getReferencedItem().getIndex());
out.writeShort(startReg);
}
public Format getFormat() {
return Format.Format3rmsf;
}

View File

@ -34,23 +34,35 @@ import org.jf.dexlib.Code.SingleRegisterInstruction;
import org.jf.dexlib.Code.LiteralInstruction;
import org.jf.dexlib.DexFile;
import org.jf.dexlib.Util.NumberUtils;
import org.jf.dexlib.Util.Output;
import org.jf.dexlib.Util.AnnotatedOutput;
public class Instruction51l extends Instruction implements SingleRegisterInstruction, LiteralInstruction {
public static final Instruction.InstructionFactory Factory = new Factory();
private byte regA;
private long litB;
public Instruction51l(Opcode opcode, short regA, long litB) {
super(opcode);
public static void emit(Output out, Opcode opcode, short regA, long litB) {
if (regA >= 1 << 8) {
throw new RuntimeException("The register number must be less than v256");
}
out.writeByte(opcode.value);
out.writeByte(regA);
out.writeLong(litB);
this.regA = (byte)regA;
this.litB = litB;
}
private Instruction51l(Opcode opcode, byte[] buffer, int bufferIndex) {
super(opcode, buffer, bufferIndex);
super(opcode);
regA = (byte)NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]);
litB = NumberUtils.decodeLong(buffer, bufferIndex + 2);
}
protected void writeInstruction(AnnotatedOutput out, int currentCodeOffset) {
out.writeByte(opcode.value);
out.writeByte(regA);
out.writeLong(litB);
}
public Format getFormat() {
@ -58,11 +70,11 @@ public class Instruction51l extends Instruction implements SingleRegisterInstruc
}
public int getRegisterA() {
return NumberUtils.decodeUnsignedByte(buffer[bufferIndex + 1]);
return regA & 0xFF;
}
public long getLiteral() {
return NumberUtils.decodeLong(buffer, bufferIndex + 2);
return litB;
}
private static class Factory implements Instruction.InstructionFactory {

View File

@ -30,28 +30,60 @@ package org.jf.dexlib.Code.Format;
import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.Code.MultiOffsetInstruction;
import org.jf.dexlib.Util.NumberUtils;
import org.jf.dexlib.Util.Output;
import org.jf.dexlib.Util.AnnotatedOutput;
import org.jf.dexlib.DexFile;
import java.util.Iterator;
public class PackedSwitchDataPseudoInstruction extends Instruction {
public class PackedSwitchDataPseudoInstruction extends Instruction implements MultiOffsetInstruction {
public static final Instruction.InstructionFactory Factory = new Factory();
private int firstKey;
private int[] targets;
@Override
public int getSize() {
return getTargetCount() * 4 + 8;
public int getSize(int offset) {
assert offset % 2 == 0;
return getTargetCount() * 4 + 8 + (offset % 4);
}
public static void emit(Output out, int firstKey, int[] targets) {
public PackedSwitchDataPseudoInstruction(int firstKey, int[] targets) {
super(Opcode.NOP);
if (targets.length > 0xFFFF) {
throw new RuntimeException("The packed-switch data contains too many elements. " +
"The maximum number of switch elements is 65535");
}
//write out padding, if necessary
if (out.getCursor() % 4 != 0) {
this.firstKey = firstKey;
this.targets = targets;
}
public PackedSwitchDataPseudoInstruction(byte[] buffer, int bufferIndex) {
super(Opcode.NOP);
byte opcodeByte = buffer[bufferIndex];
if (opcodeByte != 0x00) {
throw new RuntimeException("Invalid opcode byte for a PackedSwitchData pseudo-instruction");
}
byte subopcodeByte = buffer[bufferIndex+1];
if (subopcodeByte != 0x01) {
throw new RuntimeException("Invalid sub-opcode byte for a PackedSwitchData pseudo-instruction");
}
int targetCount = NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2);
this.firstKey = NumberUtils.decodeInt(buffer, bufferIndex + 4);
this.targets = new int[targetCount];
for (int i = 0; i<targetCount; i++) {
targets[i] = NumberUtils.decodeInt(buffer, bufferIndex + 8 + 4*i);
}
}
protected void writeInstruction(AnnotatedOutput out, int currentCodeOffset) {
//write out padding, if necessary
if (currentCodeOffset % 4 != 0) {
out.writeShort(0);
}
@ -65,17 +97,13 @@ public class PackedSwitchDataPseudoInstruction extends Instruction {
}
}
public PackedSwitchDataPseudoInstruction(byte[] buffer, int bufferIndex) {
super(Opcode.NOP, buffer, bufferIndex);
protected void annotateInstruction(AnnotatedOutput out, int currentCodeOffset) {
out.annotate(getSize(currentCodeOffset), "[0x" + Integer.toHexString(currentCodeOffset/2) + "] " +
"packed-switch-data instruction");
}
byte opcodeByte = buffer[bufferIndex++];
if (opcodeByte != 0x00) {
throw new RuntimeException("Invalid opcode byte for a PackedSwitchData pseudo-instruction");
}
byte subopcodeByte = buffer[bufferIndex];
if (subopcodeByte != 0x01) {
throw new RuntimeException("Invalid sub-opcode byte for a PackedSwitchData pseudo-instruction");
}
public void updateTarget(int targetIndex, int targetOffset) {
targets[targetIndex] = targetOffset;
}
public Format getFormat() {
@ -83,11 +111,15 @@ public class PackedSwitchDataPseudoInstruction extends Instruction {
}
public int getTargetCount() {
return NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2);
return targets.length;
}
public int getFirstKey() {
return NumberUtils.decodeInt(buffer, bufferIndex + 4);
return firstKey;
}
public int[] getTargets() {
return targets;
}
public static class PackedSwitchTarget {
@ -95,11 +127,10 @@ public class PackedSwitchDataPseudoInstruction extends Instruction {
public int target;
}
public Iterator<PackedSwitchTarget> getTargets() {
public Iterator<PackedSwitchTarget> iterateKeysAndTargets() {
return new Iterator<PackedSwitchTarget>() {
final int targetCount = getTargetCount();
int i = 0;
int position = bufferIndex + 8;
int value = getFirstKey();
PackedSwitchTarget packedSwitchTarget = new PackedSwitchTarget();
@ -110,8 +141,7 @@ public class PackedSwitchDataPseudoInstruction extends Instruction {
public PackedSwitchTarget next() {
packedSwitchTarget.value = value++;
packedSwitchTarget.target = NumberUtils.decodeInt(buffer, position);
position+=4;
packedSwitchTarget.target = targets[i];
i++;
return packedSwitchTarget;
}

View File

@ -30,21 +30,27 @@ package org.jf.dexlib.Code.Format;
import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.Code.MultiOffsetInstruction;
import org.jf.dexlib.Util.NumberUtils;
import org.jf.dexlib.Util.Output;
import org.jf.dexlib.Util.AnnotatedOutput;
import org.jf.dexlib.DexFile;
import java.util.Iterator;
public class SparseSwitchDataPseudoInstruction extends Instruction {
public class SparseSwitchDataPseudoInstruction extends Instruction implements MultiOffsetInstruction {
public static final Instruction.InstructionFactory Factory = new Factory();
private int[] keys;
private int[] targets;
@Override
public int getSize() {
return getTargetCount() * 8 + 4;
public int getSize(int offset) {
assert offset % 2 == 0;
return getTargetCount() * 8 + 4 + (offset % 4);
}
public static void emit(Output out, int[] keys, int[] targets) {
public SparseSwitchDataPseudoInstruction(int[] keys, int[] targets) {
super(Opcode.NOP);
if (keys.length != targets.length) {
throw new RuntimeException("The number of keys and offsets don't match");
}
@ -58,8 +64,35 @@ public class SparseSwitchDataPseudoInstruction extends Instruction {
"The maximum number of switch elements is 65535");
}
this.keys = keys;
this.targets = targets;
}
public SparseSwitchDataPseudoInstruction(byte[] buffer, int bufferIndex) {
super(Opcode.NOP);
byte opcodeByte = buffer[bufferIndex];
if (opcodeByte != 0x00) {
throw new RuntimeException("Invalid opcode byte for a SparseSwitchData pseudo-instruction");
}
byte subopcodeByte = buffer[bufferIndex+1];
if (subopcodeByte != 0x02) {
throw new RuntimeException("Invalid sub-opcode byte for a SparseSwitchData pseudo-instruction");
}
int targetCount = NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2);
keys = new int[targetCount];
targets = new int[targetCount];
for (int i=0; i<targetCount; i++) {
keys[i] = NumberUtils.decodeInt(buffer, bufferIndex + 4 + i*4);
targets[i] = NumberUtils.decodeInt(buffer, bufferIndex + 4 + targetCount*4 + i*4);
}
}
protected void writeInstruction(AnnotatedOutput out, int currentCodeOffset) {
//write out padding, if necessary
if (out.getCursor() % 4 != 0) {
if (currentCodeOffset % 4 != 0) {
out.writeShort(0);
}
@ -74,10 +107,7 @@ public class SparseSwitchDataPseudoInstruction extends Instruction {
for (int i = 1; i < keys.length; i++) {
key = keys[i];
if (key <= keys[i - 1]) {
throw new RuntimeException("The targets in a sparse switch block must be sorted in ascending" +
"order, by key");
}
assert key <= keys[i - 1];
out.writeInt(key);
}
@ -87,17 +117,13 @@ public class SparseSwitchDataPseudoInstruction extends Instruction {
}
}
public SparseSwitchDataPseudoInstruction(byte[] buffer, int bufferIndex) {
super(Opcode.NOP, buffer, bufferIndex);
protected void annotateInstruction(AnnotatedOutput out, int currentCodeOffset) {
out.annotate(getSize(currentCodeOffset), "[0x" + Integer.toHexString(currentCodeOffset/2) + "] " +
"sparse-switch-data instruction");
}
byte opcodeByte = buffer[bufferIndex++];
if (opcodeByte != 0x00) {
throw new RuntimeException("Invalid opcode byte for a SparseSwitchData pseudo-instruction");
}
byte subopcodeByte = buffer[bufferIndex];
if (subopcodeByte != 0x02) {
throw new RuntimeException("Invalid sub-opcode byte for a SparseSwitchData pseudo-instruction");
}
public void updateTarget(int targetIndex, int targetOffset) {
targets[targetIndex] = targetOffset;
}
public Format getFormat() {
@ -105,20 +131,26 @@ public class SparseSwitchDataPseudoInstruction extends Instruction {
}
public int getTargetCount() {
return NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2);
return targets.length;
}
public int[] getTargets() {
return targets;
}
public int[] getKeys() {
return keys;
}
public static class SparseSwitchTarget {
public int value;
public int key;
public int target;
}
public Iterator<SparseSwitchTarget> getTargets() {
public Iterator<SparseSwitchTarget> iterateKeysAndTargets() {
return new Iterator<SparseSwitchTarget>() {
final int targetCount = getTargetCount();
int i = 0;
int valuePosition = bufferIndex + 4;
int targetPosition = bufferIndex + 4 + targetCount * 4;
SparseSwitchTarget sparseSwitchTarget = new SparseSwitchTarget();
@ -127,10 +159,8 @@ public class SparseSwitchDataPseudoInstruction extends Instruction {
}
public SparseSwitchTarget next() {
sparseSwitchTarget.value = NumberUtils.decodeInt(buffer, valuePosition);
sparseSwitchTarget.target = NumberUtils.decodeInt(buffer, targetPosition);
valuePosition+=4;
targetPosition+=4;
sparseSwitchTarget.key = keys[i];
sparseSwitchTarget.target = targets[i];
i++;
return sparseSwitchTarget;
}

View File

@ -29,12 +29,12 @@
package org.jf.dexlib.Code.Format;
import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.Util.AnnotatedOutput;
/**
* This represents a "fixed" odexed instruction, where the object register is always null and so the correct type
* can't be determined. How this is handled is "implementation dependent". baksmali just replaces it with a call to
* object->hashCode(). Since the object register is always null, this will have the same effect as tring to access
* whatever method/field that was trying to be accessed - namely, a NPE
* can't be determined. Typically, these are replaced by an equivalent instruction that would have the same
* effect (namely, an NPE)
*/
public class UnresolvedNullReference extends Instruction {
public final Instruction OriginalInstruction;
@ -47,9 +47,13 @@ public class UnresolvedNullReference extends Instruction {
this.ObjectRegisterNum = objectRegisterNumber;
}
protected void writeInstruction(AnnotatedOutput out, int currentCodeOffset) {
throw new RuntimeException("Cannot rewrite an instruction that couldn't be deodexed");
}
@Override
public int getSize() {
return OriginalInstruction.getSize();
public int getSize(int offset) {
return OriginalInstruction.getSize(offset);
}
public Format getFormat() {

View File

@ -29,44 +29,38 @@
package org.jf.dexlib.Code;
import org.jf.dexlib.*;
import org.jf.dexlib.Util.ByteArrayInput;
import org.jf.dexlib.Util.AnnotatedOutput;
import org.jf.dexlib.Code.Format.Format;
public abstract class Instruction {
public final Opcode opcode;
protected final byte[] buffer;
protected final int bufferIndex;
public int getSize() {
public int getSize(int offset) {
return opcode.format.size;
}
protected Instruction(Opcode opcode) {
this.opcode = opcode;
this.bufferIndex = 0;
this.buffer = new byte[opcode.format.size];
}
protected Instruction(Opcode opcode, int bufferSize) {
this.opcode = opcode;
this.bufferIndex = 0;
this.buffer = new byte[bufferSize];
}
protected Instruction(Opcode opcode, byte[] buffer, int bufferIndex) {
this.opcode = opcode;
this.buffer = buffer;
this.bufferIndex = bufferIndex;
if (buffer[bufferIndex] != opcode.value) {
throw new RuntimeException("The given opcode doesn't match the opcode byte");
}
}
public abstract Format getFormat();
public int write(AnnotatedOutput out, int currentCodeOffset) {
if (out.annotates()) {
annotateInstruction(out, currentCodeOffset);
}
writeInstruction(out, currentCodeOffset);
return currentCodeOffset + getSize(currentCodeOffset);
}
protected void annotateInstruction(AnnotatedOutput out, int currentCodeOffset) {
out.annotate(getSize(currentCodeOffset), "[0x" + Integer.toHexString(currentCodeOffset/2) + "] " +
opcode.name + " instruction");
}
protected abstract void writeInstruction(AnnotatedOutput out, int currentCodeOffset);
public static interface InstructionFactory {
public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex);
}

View File

@ -103,50 +103,49 @@ public class InstructionIterator {
}
public static void IterateInstructions(DexFile dexFile, byte[] insns, ProcessInstructionDelegate delegate) {
int insnsPosition = 0;
int currentCodeOffset = 0;
while (insnsPosition < insns.length) {
Opcode opcode = Opcode.getOpcodeByValue(insns[insnsPosition]);
while (currentCodeOffset < insns.length) {
Opcode opcode = Opcode.getOpcodeByValue(insns[currentCodeOffset]);
Instruction instruction = null;
if (opcode == null) {
throw new RuntimeException("Unknown opcode: " + Hex.u1(insns[insnsPosition]));
throw new RuntimeException("Unknown opcode: " + Hex.u1(insns[currentCodeOffset]));
}
if (opcode == Opcode.NOP) {
byte secondByte = insns[insnsPosition+1];
byte secondByte = insns[currentCodeOffset+1];
switch (secondByte) {
case 0:
{
instruction = new Instruction10x(Opcode.NOP, insns, insnsPosition);
instruction = new Instruction10x(Opcode.NOP, insns, currentCodeOffset);
break;
}
case 1:
{
insnsPosition += insnsPosition & 0x01;
instruction = new PackedSwitchDataPseudoInstruction(insns, insnsPosition);
instruction = new PackedSwitchDataPseudoInstruction(insns, currentCodeOffset);
break;
}
case 2:
{
insnsPosition += insnsPosition & 0x01;
instruction = new SparseSwitchDataPseudoInstruction(insns, insnsPosition);
instruction = new SparseSwitchDataPseudoInstruction(insns, currentCodeOffset);
break;
}
case 3:
{
insnsPosition += insnsPosition & 0x01;
instruction = new ArrayDataPseudoInstruction(insns, insnsPosition);
instruction = new ArrayDataPseudoInstruction(insns, currentCodeOffset);
break;
}
}
} else {
instruction = opcode.format.Factory.makeInstruction(dexFile, opcode, insns, insnsPosition);
instruction = opcode.format.Factory.makeInstruction(dexFile, opcode, insns, currentCodeOffset);
}
delegate.ProcessInstruction(insnsPosition, instruction);
insnsPosition += instruction.getSize();
assert instruction != null;
delegate.ProcessInstruction(currentCodeOffset, instruction);
currentCodeOffset += instruction.getSize(currentCodeOffset);
}
}

View File

@ -41,14 +41,14 @@ public abstract class InstructionWithReference extends Instruction {
}
protected InstructionWithReference(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
super(opcode, buffer, bufferIndex);
super(opcode);
int itemIndex = getReferencedItemIndex();
int itemIndex = getReferencedItemIndex(buffer, bufferIndex);
lookupReferencedItem(dexFile, opcode, itemIndex);
}
protected int getReferencedItemIndex() {
return NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2);
protected int getReferencedItemIndex(byte[] buffer, int bufferIndex) {
return NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2);
}
public Item getReferencedItem() {

View File

@ -0,0 +1,34 @@
/*
* [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.Code;
public interface MultiOffsetInstruction {
public int[] getTargets();
public void updateTarget(int targetIndex, int targetOffset);
}

View File

@ -30,4 +30,5 @@ package org.jf.dexlib.Code;
public interface OffsetInstruction {
public int getOffset();
public void updateOffset(int offset);
}

View File

@ -29,21 +29,20 @@
package org.jf.dexlib;
import org.jf.dexlib.Code.*;
import org.jf.dexlib.Util.AnnotatedOutput;
import org.jf.dexlib.Util.Input;
import org.jf.dexlib.Util.SparseArray;
import org.jf.dexlib.Util.Leb128Utils;
import org.jf.dexlib.Code.Format.Instruction20t;
import org.jf.dexlib.Code.Format.Instruction30t;
import org.jf.dexlib.Util.*;
import org.jf.dexlib.Debug.DebugInstructionIterator;
import java.util.List;
import java.util.LinkedList;
import java.util.ArrayList;
public class CodeItem extends Item<CodeItem> {
private int registerCount;
private int inWords;
private int outWords;
private DebugInfoItem debugInfo;
private byte[] encodedInstructions;
private Item[] referencedItems;
private Instruction[] instructions;
private TryItem[] tries;
private EncodedCatchHandler[] encodedCatchHandlers;
@ -64,8 +63,7 @@ public class CodeItem extends Item<CodeItem> {
* @param inWords the number of 2-byte words that the parameters to the method containing this code take
* @param outWords the maximum number of 2-byte words for the arguments of any method call in this code
* @param debugInfo the debug information for this code/method
* @param encodedInstructions the instructions, encoded as a byte array
* @param referencedItems an array of the items referenced by instructions, in order of occurance in the code
* @param instructions the instructions for this code item
* @param tries an array of the tries defined for this code/method
* @param encodedCatchHandlers an array of the exception handlers defined for this code/method
*/
@ -74,8 +72,7 @@ public class CodeItem extends Item<CodeItem> {
int inWords,
int outWords,
DebugInfoItem debugInfo,
byte[] encodedInstructions,
Item[] referencedItems,
Instruction[] instructions,
TryItem[] tries,
EncodedCatchHandler[] encodedCatchHandlers) {
super(dexFile);
@ -87,8 +84,8 @@ public class CodeItem extends Item<CodeItem> {
if (debugInfo != null) {
debugInfo.setParent(this);
}
this.encodedInstructions = encodedInstructions;
this.referencedItems = referencedItems;
this.instructions = instructions;
this.tries = tries;
this.encodedCatchHandlers = encodedCatchHandlers;
}
@ -100,9 +97,7 @@ public class CodeItem extends Item<CodeItem> {
* @param inWords the number of 2-byte words that the parameters to the method containing this code take
* @param outWords the maximum number of 2-byte words for the arguments of any method call in this code
* @param debugInfo the debug information for this code/method
* @param encodedInstructions the instructions, encoded as a byte array
* @param referencedItems a list of the items referenced by instructions, in order of occurance in the code,
* or null if none
* @param instructions the instructions for this code item
* @param tries a list of the tries defined for this code/method or null if none
* @param encodedCatchHandlers a list of the exception handlers defined for this code/method or null if none
* @return a new <code>CodeItem</code> with the given values.
@ -112,18 +107,12 @@ public class CodeItem extends Item<CodeItem> {
int inWords,
int outWords,
DebugInfoItem debugInfo,
byte[] encodedInstructions,
List<Item> referencedItems,
List<Instruction> instructions,
List<TryItem> tries,
List<EncodedCatchHandler> encodedCatchHandlers) {
Item[] referencedItemsArray = null;
TryItem[] triesArray = null;
EncodedCatchHandler[] encodedCatchHandlersArray = null;
if (referencedItems != null && referencedItems.size() > 0) {
referencedItemsArray = new Item[referencedItems.size()];
referencedItems.toArray(referencedItemsArray);
}
Instruction[] instructionsArray = null;
if (tries != null && tries.size() > 0) {
triesArray = new TryItem[tries.size()];
@ -135,8 +124,13 @@ public class CodeItem extends Item<CodeItem> {
encodedCatchHandlers.toArray(encodedCatchHandlersArray);
}
CodeItem codeItem = new CodeItem(dexFile, registerCount, inWords, outWords, debugInfo, encodedInstructions,
referencedItemsArray, triesArray, encodedCatchHandlersArray);
if (instructions != null && instructions.size() > 0) {
instructionsArray = new Instruction[instructions.size()];
instructions.toArray(instructionsArray);
}
CodeItem codeItem = new CodeItem(dexFile, registerCount, inWords, outWords, debugInfo, instructionsArray,
triesArray, encodedCatchHandlersArray);
return dexFile.CodeItemsSection.intern(codeItem);
}
@ -152,8 +146,20 @@ public class CodeItem extends Item<CodeItem> {
this.debugInfo.setParent(this);
}
int instructionCount = in.readInt();
this.encodedInstructions = in.readBytes(instructionCount * 2);
this.referencedItems = InstructionReader.getReferencedItems(encodedInstructions, dexFile);
final ArrayList<Instruction> instructionList = new ArrayList<Instruction>();
byte[] encodedInstructions = in.readBytes(instructionCount * 2);
InstructionIterator.IterateInstructions(dexFile, encodedInstructions,
new InstructionIterator.ProcessInstructionDelegate() {
public void ProcessInstruction(int index, Instruction instruction) {
instructionList.add(instruction);
}
});
this.instructions = new Instruction[instructionList.size()];
instructionList.toArray(instructions);
if (triesCount > 0) {
in.alignTo(4);
@ -187,9 +193,10 @@ public class CodeItem extends Item<CodeItem> {
/** {@inheritDoc} */
protected int placeItem(int offset) {
offset += 16 + encodedInstructions.length;
offset += 16 + getInstructionsLength();
if (tries != null && tries.length > 0) {
if (encodedInstructions.length % 4 != 0) {
if (offset % 4 != 0) {
offset+=2;
}
@ -205,6 +212,8 @@ public class CodeItem extends Item<CodeItem> {
/** {@inheritDoc} */
protected void writeItem(final AnnotatedOutput out) {
int instructionsLength = getInstructionsLength()/2;
if (out.annotates()) {
out.annotate(0, parent.method.getMethodString());
out.annotate(2, "registers_size: 0x" + Integer.toHexString(registerCount) + " (" + registerCount + ")");
@ -217,37 +226,8 @@ public class CodeItem extends Item<CodeItem> {
} else {
out.annotate(4, "debug_info_off: 0x" + debugInfo.getOffset());
}
out.annotate(4, "insns_size: 0x" + Integer.toHexString(encodedInstructions.length / 2) + " (" +
(encodedInstructions.length / 2) + ")");
InstructionIterator.IterateInstructions(encodedInstructions,
new InstructionIterator.ProcessRawInstructionDelegate() {
public void ProcessNormalInstruction(Opcode opcode, int index) {
out.annotate(opcode.format.size, "[0x" + Integer.toHexString(index/2) + "] " + opcode.name +
" instruction");
}
public void ProcessReferenceInstruction(Opcode opcode, int index) {
out.annotate(opcode.format.size, "[0x" + Integer.toHexString(index/2) + "] " + opcode.name +
" instruction");
}
public void ProcessPackedSwitchInstruction(int index, int targetCount, int instructionLength) {
out.annotate(instructionLength, "[0x" + Integer.toHexString(index/2) + "] " +
"packed_switch instruction");
}
public void ProcessSparseSwitchInstruction(int index, int targetCount, int instructionLength) {
out.annotate(instructionLength, "[0x" + Integer.toHexString(index/2) + "] " +
"sparse_switch instruction");
}
public void ProcessFillArrayDataInstruction(int index, int elementWidth, int elementCount,
int instructionLength) {
out.annotate(instructionLength, "[0x" + Integer.toHexString(index/2) + "] " +
"fill_array_data instruction");
}
});
out.annotate(4, "insns_size: 0x" + Integer.toHexString(instructionsLength) + " (" +
(instructionsLength) + ")");
}
out.writeShort(registerCount);
@ -263,12 +243,22 @@ public class CodeItem extends Item<CodeItem> {
} else {
out.writeInt(debugInfo.getOffset());
}
out.writeInt(encodedInstructions.length / 2);
InstructionWriter.writeInstructions(encodedInstructions, referencedItems, out);
int currentCodeOffset = 0;
for (Instruction instruction: instructions) {
currentCodeOffset += instruction.getSize(currentCodeOffset);
}
out.writeInt(instructionsLength);
currentCodeOffset = 0;
for (Instruction instruction: instructions) {
currentCodeOffset = instruction.write(out, currentCodeOffset);
}
if (tries != null && tries.length > 0) {
if (out.annotates()) {
if ((encodedInstructions.length % 4) != 0) {
if ((currentCodeOffset % 4) != 0) {
out.annotate("padding");
out.writeShort(0);
}
@ -293,7 +283,7 @@ public class CodeItem extends Item<CodeItem> {
out.deindent();
}
} else {
if ((encodedInstructions.length % 4) != 0) {
if ((currentCodeOffset % 4) != 0) {
out.writeShort(0);
}
@ -343,10 +333,10 @@ public class CodeItem extends Item<CodeItem> {
}
/**
* @return a byte array containing the encoded instructions
* @return an array of the instructions in this code item
*/
public byte[] getEncodedInstructions() {
return encodedInstructions;
public Instruction[] getInstructions() {
return instructions;
}
/**
@ -388,24 +378,268 @@ public class CodeItem extends Item<CodeItem> {
/**
* Used by OdexUtil to update this <code>CodeItem</code> with a deodexed version of the instructions
* @param newEncodedInstructions
* @param newInstructions the new instructions to use for this code item
*/
public void updateCode(byte[] newEncodedInstructions) {
final LinkedList<Item> referencedItemsList = new LinkedList<Item>();
public void updateCode(Instruction[] newInstructions) {
this.instructions = newInstructions;
}
private int getInstructionsLength() {
int offset = 0;
for (Instruction instruction: instructions) {
offset += instruction.getSize(offset);
}
return offset;
}
InstructionIterator.IterateInstructions(dexFile, newEncodedInstructions,
new InstructionIterator.ProcessInstructionDelegate() {
public void ProcessInstruction(int index, Instruction instruction) {
if (instruction.opcode.referenceType != ReferenceType.none) {
referencedItemsList.add(((InstructionWithReference)instruction).getReferencedItem());
}
/**
* Go through the instructions and perform any of the following fixes that are applicable
* - Replace const-string instruction with const-string/jumbo, when the string index is too big
* - Replace goto and goto/16 with a larger version of goto, when the target is too far away
* TODO: we should be able to replace if-* instructions with targets that are too far away with a negated if followed by a goto/32 to the original target
*
* The above fixes are applied iteratively, until no more fixes have been performed
*/
public void fixInstructions() {
boolean didSomething = false;
int currentCodeOffset = 0;
for (int i=0; i<instructions.length; i++) {
Instruction instruction = instructions[i];
if (instruction.opcode == Opcode.GOTO) {
int offset = ((OffsetInstruction)instruction).getOffset();
if (((byte)offset) != offset) {
//the offset doesn't fit within a byte, we need to upgrade to a goto/16 or goto/32
if ((short)offset == offset) {
//the offset fits in a short, so upgrade to a goto/16 h
replaceInstructionAtOffset(currentCodeOffset, new Instruction20t(Opcode.GOTO_16, offset));
}
});
else {
//The offset won't fit into a short, we have to upgrade to a goto/32
replaceInstructionAtOffset(currentCodeOffset, new Instruction30t(Opcode.GOTO_32, offset));
}
}
} else if (instruction.opcode == Opcode.GOTO_16) {
int offset = ((OffsetInstruction)instruction).getOffset();
referencedItems = new Item[referencedItemsList.size()];
referencedItemsList.toArray(referencedItems);
encodedInstructions = newEncodedInstructions;
if (((short)offset) != offset) {
//the offset doesn't fit within a short, we need to upgrade to a goto/32
replaceInstructionAtOffset(currentCodeOffset, new Instruction30t(Opcode.GOTO_32, offset));
}
}
currentCodeOffset += instruction.getSize(currentCodeOffset);
}
}
private void replaceInstructionAtOffset(int offset, Instruction replacementInstruction) {
Instruction originalInstruction = null;
int[] originalInstructionOffsets = new int[instructions.length];
SparseIntArray originalSwitchOffsetByOriginalSwitchDataOffset = new SparseIntArray();
int currentCodeOffset = 0;
int instructionIndex = 0;
for (int i=0; i<instructions.length; i++) {
Instruction instruction = instructions[i];
if (currentCodeOffset == offset) {
originalInstruction = instruction;
instructionIndex = i;
}
if (instruction.opcode == Opcode.PACKED_SWITCH || instruction.opcode == Opcode.SPARSE_SWITCH) {
OffsetInstruction offsetInstruction = (OffsetInstruction)instruction;
int switchDataOffset = currentCodeOffset + offsetInstruction.getOffset() * 2;
if (originalSwitchOffsetByOriginalSwitchDataOffset.indexOfKey(switchDataOffset) < 0) {
originalSwitchOffsetByOriginalSwitchDataOffset.put(switchDataOffset, currentCodeOffset);
}
}
originalInstructionOffsets[i] = currentCodeOffset;
currentCodeOffset += instruction.getSize(currentCodeOffset);
}
if (originalInstruction == null) {
throw new RuntimeException("There is no instruction at offset " + offset);
}
instructions[instructionIndex] = replacementInstruction;
//if we're replacing the instruction with one of the same size, we don't have to worry about fixing
//up any offsets
if (originalInstruction.getSize(offset) == replacementInstruction.getSize(offset)) {
return;
}
final SparseIntArray originalOffsetsByNewOffset = new SparseIntArray();
final SparseIntArray newOffsetsByOriginalOffset = new SparseIntArray();
currentCodeOffset = 0;
for (int i=0; i<instructions.length; i++) {
Instruction instruction = instructions[i];
int originalOffset = originalInstructionOffsets[i];
originalOffsetsByNewOffset.append(currentCodeOffset, originalOffset);
newOffsetsByOriginalOffset.append(originalOffset, currentCodeOffset);
currentCodeOffset += instruction.getSize(currentCodeOffset);
}
//update any "offset" instructions, or switch data instructions
currentCodeOffset = 0;
for (int i=0; i<instructions.length; i++) {
Instruction instruction = instructions[i];
if (instruction instanceof OffsetInstruction) {
OffsetInstruction offsetInstruction = (OffsetInstruction)instruction;
assert originalOffsetsByNewOffset.indexOfKey(currentCodeOffset) >= 0;
int originalOffset = originalOffsetsByNewOffset.get(currentCodeOffset);
int originalInstructionTarget = originalOffset + offsetInstruction.getOffset() * 2;
assert newOffsetsByOriginalOffset.indexOfKey(originalInstructionTarget) >= 0;
int newInstructionTarget = newOffsetsByOriginalOffset.get(originalInstructionTarget);
if (newInstructionTarget != originalInstructionTarget) {
offsetInstruction.updateOffset(newInstructionTarget);
}
} else if (instruction instanceof MultiOffsetInstruction) {
MultiOffsetInstruction multiOffsetInstruction = (MultiOffsetInstruction)instruction;
assert originalOffsetsByNewOffset.indexOfKey(currentCodeOffset) >= 0;
int originalDataOffset = originalOffsetsByNewOffset.get(currentCodeOffset);
int originalSwitchOffset = originalSwitchOffsetByOriginalSwitchDataOffset.get(originalDataOffset);
if (originalSwitchOffset == 0) {
//TODO: is it safe to skip an unreferenced switch data instruction? Or should it throw an exception?
continue;
}
int[] targets = multiOffsetInstruction.getTargets();
for (int t=0; t<targets.length; t++) {
int originalTargetOffset = originalSwitchOffset + targets[t];
assert newOffsetsByOriginalOffset.indexOfKey(originalTargetOffset) >= 0;
int newTargetOffset = newOffsetsByOriginalOffset.get(originalTargetOffset);
if (newTargetOffset != originalTargetOffset) {
multiOffsetInstruction.updateTarget(t, newTargetOffset);
}
}
}
currentCodeOffset += instruction.getSize(currentCodeOffset);
}
final byte[] encodedDebugInfo = debugInfo.getEncodedDebugInfo();
ByteArrayInput debugInput = new ByteArrayInput(encodedDebugInfo);
DebugInstructionFixer debugInstructionFixer = new DebugInstructionFixer(encodedDebugInfo,
newOffsetsByOriginalOffset, originalOffsetsByNewOffset);
DebugInstructionIterator.IterateInstructions(debugInput, debugInstructionFixer);
debugInfo.setEncodedDebugInfo(debugInstructionFixer.result);
for (EncodedCatchHandler encodedCatchHandler: encodedCatchHandlers) {
if (encodedCatchHandler.catchAllHandlerAddress != -1) {
assert newOffsetsByOriginalOffset.indexOfKey(encodedCatchHandler.catchAllHandlerAddress) >= 0;
encodedCatchHandler.catchAllHandlerAddress =
newOffsetsByOriginalOffset.get(encodedCatchHandler.catchAllHandlerAddress);
}
for (EncodedTypeAddrPair handler: encodedCatchHandler.handlers) {
handler.handlerAddress = newOffsetsByOriginalOffset.get(handler.handlerAddress);
}
}
}
private class DebugInstructionFixer extends DebugInstructionIterator.ProcessRawDebugInstructionDelegate {
private int address = 0;
private SparseIntArray newOffsetsByOriginalOffset;
private SparseIntArray originalOffsetByNewOffset;
private final byte[] originalEncodedDebugInfo;
public byte[] result = null;
public DebugInstructionFixer(byte[] originalEncodedDebugInfo, SparseIntArray newOffsetsbyOriginalOffset,
SparseIntArray originalOffsetsByNewOffset) {
this.newOffsetsByOriginalOffset = newOffsetsByOriginalOffset;
this.originalOffsetByNewOffset = originalOffsetByNewOffset;
this.originalEncodedDebugInfo = originalEncodedDebugInfo;
}
@Override
public void ProcessAdvancePC(int startOffset, int length, int addressDiff) {
if (result != null) {
return;
}
int newOffset = newOffsetsByOriginalOffset.get((address + addressDiff)*2, -1);
assert newOffset != -1;
newOffset = newOffset / 2;
if (newOffset != address) {
int newAddressDiff = addressDiff + newOffset - address;
assert newAddressDiff > 0;
int addressDiffSize = Leb128Utils.unsignedLeb128Size(newAddressDiff);
result = new byte[originalEncodedDebugInfo.length + addressDiffSize - (length - 1)];
System.arraycopy(originalEncodedDebugInfo, 0, result, 0, startOffset);
originalEncodedDebugInfo[startOffset] = 0x01; //DBG_ADVANCE_PC debug opcode
Leb128Utils.writeUnsignedLeb128(newAddressDiff, originalEncodedDebugInfo, startOffset+1);
System.arraycopy(originalEncodedDebugInfo, startOffset+length, originalEncodedDebugInfo,
startOffset + addressDiffSize + 1,
originalEncodedDebugInfo.length - (startOffset + addressDiffSize + 1));
}
address += addressDiff;
}
@Override
public void ProcessSpecialOpcode(int startOffset, int debugOpcode, int lineDelta,
int addressDelta) {
if (result != null) {
return;
}
address += addressDelta;
int newOffset = newOffsetsByOriginalOffset.get(address*2, -1);
assert newOffset != -1;
newOffset = newOffset / 2;
if (newOffset != address) {
int newAddressDelta = addressDelta + newOffset - address;
assert newAddressDelta > 0;
//if the new address delta won't fit in the special opcode, we need to insert
//an additional DBG_ADVANCE_PC opcode
if (lineDelta < 2 && newAddressDelta > 16 || lineDelta > 1 && newAddressDelta > 15) {
int additionalAddressDelta = newOffset - address;
int additionalAddressDeltaSize = Leb128Utils.signedLeb128Size(additionalAddressDelta);
result = new byte[result.length + additionalAddressDeltaSize + 1];
System.arraycopy(originalEncodedDebugInfo, 0, result, 0, startOffset);
result[startOffset] = 0x01; //DBG_ADVANCE_PC
Leb128Utils.writeUnsignedLeb128(additionalAddressDelta, result, startOffset+1);
System.arraycopy(originalEncodedDebugInfo, startOffset, result,
startOffset+additionalAddressDeltaSize+1,
result.length - (startOffset+additionalAddressDeltaSize+1));
} else {
result = new byte[result.length];
System.arraycopy(originalEncodedDebugInfo, 0, result, 0, result.length);
result[startOffset] = DebugInfoBuilder.calculateSpecialOpcode(lineDelta,
newAddressDelta);
}
}
}
}
public static class TryItem {
@ -481,7 +715,7 @@ public class CodeItem extends Item<CodeItem> {
* The address within the code (in 2-byte words) for the catch all handler, or -1 if there is no catch all
* handler
*/
public final int catchAllHandlerAddress;
private int catchAllHandlerAddress;
//TODO: would it be possible to get away without having these? and generate/create these values while writing?
private int baseOffset;
@ -524,6 +758,14 @@ public class CodeItem extends Item<CodeItem> {
}
}
/**
* Returns the "Catch All" handler address for this <code>EncodedCatchHandler</code>
* @return
*/
public int getCatchAllHandlerAddress() {
return catchAllHandlerAddress;
}
/**
* @return the offset of this <code>EncodedCatchHandler</code> from the beginning of the
* encoded_catch_handler_list structure
@ -643,7 +885,7 @@ public class CodeItem extends Item<CodeItem> {
/**
* The address (in 2-byte words) in the code of the handler
*/
public final int handlerAddress;
private int handlerAddress;
/**
* Constructs a new <code>EncodedTypeAddrPair</code> with the given values
@ -691,6 +933,10 @@ public class CodeItem extends Item<CodeItem> {
}
}
public int getHandlerAddress() {
return handlerAddress;
}
@Override
public int hashCode() {
return exceptionType.hashCode() * 31 + handlerAddress;

View File

@ -269,6 +269,17 @@ public class DebugInfoItem extends Item<DebugInfoItem> {
}
}
/**
* Replaces the encoded debug info for this DebugInfoItem. It is expected that the new debug info is compatible
* with the existing information, i.e. lineStart, referencedItems, parameterNames
* @param encodedDebugInfo the new encoded debug info
*/
protected void setEncodedDebugInfo(byte[] encodedDebugInfo) {
//TODO: I would rather replace this method with some way of saying "The (code) instruction at offset changed from A bytes to B bytes. Fixup the debug info accordingly"
this.encodedDebugInfo = encodedDebugInfo;
}
/**
* Helper method that writes the item, without writing annotations
* @param out the AnnotatedOutput object

View File

@ -377,7 +377,26 @@ public class DexFile
in.setCursor(mapOffset);
MapItem.readFrom(in, 0, readContext);
for (Section section: sectionsByType) {
Section sections[] = new Section[] {
StringIdsSection,
TypeIdsSection,
ProtoIdsSection,
FieldIdsSection,
MethodIdsSection,
ClassDefsSection,
StringDataSection,
TypeListsSection,
AnnotationSetRefListsSection,
AnnotationSetsSection,
ClassDataSection,
CodeItemsSection,
AnnotationDirectoriesSection,
DebugInfoItemsSection,
AnnotationsSection,
EncodedArraysSection,
};
for (Section section: sections) {
if (section == null) {
continue;
}

View File

@ -32,8 +32,6 @@ import org.jf.dexlib.Util.AnnotatedOutput;
import org.jf.dexlib.Util.Input;
import org.jf.dexlib.Util.Utf8Utils;
import java.io.UnsupportedEncodingException;
public class HeaderItem extends Item<HeaderItem> {
/**
* the file format magic number, represented as the

View File

@ -30,8 +30,6 @@ package org.jf.dexlib;
import org.jf.dexlib.Util.Input;
import java.io.UnsupportedEncodingException;
public class OdexHeaderItem {
/**

View File

@ -111,7 +111,7 @@ public class ByteArrayInput
}
cursor = end;
return (int)((data[readAt] & 0xff) +
return ((data[readAt] & 0xff) +
((data[readAt + 1] & 0xff) << 8));
}

View File

@ -180,6 +180,10 @@ public class DebugInfoBuilder
referencedItemsArray);
}
public static byte calculateSpecialOpcode(int lineDelta, int addressDelta) {
return (byte)(FIRST_SPECIAL + (addressDelta * LINE_RANGE) + (lineDelta - LINE_BASE));
}
private interface Event
{
int getAddress();
@ -280,10 +284,6 @@ public class DebugInfoBuilder
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

View File

@ -142,30 +142,28 @@ public class DeodexUtil {
final ArrayList<insn> insns = new ArrayList<insn>();
final SparseArray<insn> insnsMap = new SparseArray<insn>();
byte[] encodedInstructions = codeItem.getEncodedInstructions().clone();
InstructionIterator.IterateInstructions(codeItem.getDexFile(), encodedInstructions,
new InstructionIterator.ProcessInstructionDelegate() {
public void ProcessInstruction(int index, Instruction instruction) {
insn i = new insn(codeItem, instruction, insnsMap, index/2);
insns.add(i);
insnsMap.append(index/2, i);
}
});
int currentCodeOffset = 0;
for (Instruction instruction: codeItem.getInstructions()) {
insn ins = new insn(codeItem, instruction, insnsMap, currentCodeOffset/2);
insns.add(ins);
insnsMap.append(currentCodeOffset/2, ins);
currentCodeOffset += instruction.getSize(currentCodeOffset);
}
if (codeItem.getTries() != null) {
for (CodeItem.TryItem tryItem: codeItem.getTries()) {
insn[] handlers;
if (tryItem.encodedCatchHandler.catchAllHandlerAddress != -1) {
if (tryItem.encodedCatchHandler.getCatchAllHandlerAddress() != -1) {
handlers = new insn[tryItem.encodedCatchHandler.handlers.length + 1];
handlers[handlers.length - 1] = insnsMap.get(tryItem.encodedCatchHandler.catchAllHandlerAddress);
handlers[handlers.length - 1] =
insnsMap.get(tryItem.encodedCatchHandler.getCatchAllHandlerAddress());
} else {
handlers = new insn[tryItem.encodedCatchHandler.handlers.length];
}
for (int i=0; i<tryItem.encodedCatchHandler.handlers.length; i++) {
handlers[i] = insnsMap.get(tryItem.encodedCatchHandler.handlers[i].handlerAddress);
handlers[i] = insnsMap.get(tryItem.encodedCatchHandler.handlers[i].getHandlerAddress());
}
int insnoffset = tryItem.startAddress;
@ -174,7 +172,7 @@ public class DeodexUtil {
i.exceptionHandlers = handlers;
insnoffset += i.instruction.getSize()/2;
insnoffset += i.instruction.getSize(insnoffset*2)/2;
}
}
}
@ -216,7 +214,7 @@ public class DeodexUtil {
System.err.println("warning: could not fully deodex the method " +
codeItem.getParent().method.getContainingClass().getTypeDescriptor() + "->" +
codeItem.getParent().method.getMethodName() +
codeItem.getParent().method.getPrototype().getPrototypeString());
codeItem.getParent().method.getPrototype().getPrototypeString());
}
List<Instruction> instructions = new ArrayList<Instruction>(insns.size());
@ -226,7 +224,7 @@ public class DeodexUtil {
instructions.add(new DeadInstruction(i.fixedInstruction));
} else {
instructions.add(new DeadInstruction(i.instruction));
}
}
} else if (i.instruction.opcode.odexOnly) {
assert i.fixedInstruction != null;
instructions.add(i.fixedInstruction);
@ -267,13 +265,13 @@ public class DeodexUtil {
inlineMethod.methodIdItem);
insn nextInstruction = i.getInstructionAtOffset(i.offset + i.instruction.getSize()/2);
insn nextInstruction = i.getInstructionAtOffset(i.offset + i.instruction.getSize(i.offset*2)/2);
assert nextInstruction != null;
if (nextInstruction.instruction.opcode == Opcode.MOVE_RESULT_OBJECT) {
nextInstruction.registerReferenceType =
inlineMethod.methodIdItem.getPrototype().getReturnType().getTypeDescriptor();
}
return true;
}
case INVOKE_DIRECT_EMPTY:
@ -406,7 +404,7 @@ public class DeodexUtil {
i.propogateDeadness();
return true;
}
if (regType != RegisterType.Reference) {
return false;
}
@ -449,7 +447,7 @@ public class DeodexUtil {
i.fixedInstruction = new UnresolvedNullReference(i.instruction, registerNum);
return true;
}
if (regType != RegisterType.Reference) {
return false;
}
@ -489,7 +487,7 @@ public class DeodexUtil {
}
i.fixedInstruction = new Instruction22csf(opcode, (Instruction22cs)i.instruction, field);
return true;
}
case IPUT_WIDE_QUICK:
@ -511,7 +509,7 @@ public class DeodexUtil {
i.fixedInstruction = new UnresolvedNullReference(i.instruction, registerNum);
return true;
}
if (regType != RegisterType.Reference) {
return false;
}
@ -553,7 +551,7 @@ public class DeodexUtil {
i.fixedInstruction = new UnresolvedNullReference(i.instruction, registerNum);
return true;
}
if (regType != RegisterType.Reference) {
return false;
}
@ -615,7 +613,7 @@ public class DeodexUtil {
i.fixedInstruction = new Instruction35msf(Opcode.INVOKE_VIRTUAL, (Instruction35ms)i.instruction,
method);
insn nextInstruction = i.getInstructionAtOffset(i.offset + i.instruction.getSize()/2);
insn nextInstruction = i.getInstructionAtOffset(i.offset + i.instruction.getSize(i.offset*2)/2);
assert nextInstruction != null;
if (nextInstruction.instruction.opcode == Opcode.MOVE_RESULT_OBJECT) {
nextInstruction.updateRegisterReferenceType(
@ -662,7 +660,7 @@ public class DeodexUtil {
i.fixedInstruction = new Instruction3rmsf(Opcode.INVOKE_VIRTUAL_RANGE, (Instruction3rms)i.instruction,
method);
insn nextInstruction = i.getInstructionAtOffset(i.offset + i.instruction.getSize()/2);
insn nextInstruction = i.getInstructionAtOffset(i.offset + i.instruction.getSize(i.offset*2)/2);
assert nextInstruction != null;
if (nextInstruction.instruction.opcode == Opcode.MOVE_RESULT_OBJECT) {
nextInstruction.updateRegisterReferenceType(
@ -687,10 +685,10 @@ public class DeodexUtil {
//and let the caller choose which "default" method to call in this case
if (regType == RegisterType.Null) {
i.fixedInstruction = new UnresolvedNullReference(i.instruction, registerNum);
//we need to mark any following instructions as dead
//we need to mark any following instructions as dead
i.propogateDeadness();
return true;
}
}
if (regType != RegisterType.Reference) {
return false;
@ -710,7 +708,7 @@ public class DeodexUtil {
i.fixedInstruction = new Instruction35msf(Opcode.INVOKE_SUPER, (Instruction35ms)i.instruction,
method);
insn nextInstruction = i.getInstructionAtOffset(i.offset + i.instruction.getSize()/2);
insn nextInstruction = i.getInstructionAtOffset(i.offset + i.instruction.getSize(i.offset*2)/2);
assert nextInstruction != null;
if (nextInstruction.instruction.opcode == Opcode.MOVE_RESULT_OBJECT) {
nextInstruction.updateRegisterReferenceType(
@ -757,7 +755,7 @@ public class DeodexUtil {
i.fixedInstruction = new Instruction3rmsf(Opcode.INVOKE_SUPER_RANGE, (Instruction3rms)i.instruction,
method);
insn nextInstruction = i.getInstructionAtOffset(i.offset + i.instruction.getSize()/2);
insn nextInstruction = i.getInstructionAtOffset(i.offset + i.instruction.getSize(i.offset*2)/2);
assert nextInstruction != null;
if (nextInstruction.instruction.opcode == Opcode.MOVE_RESULT_OBJECT) {
nextInstruction.updateRegisterReferenceType(
@ -779,7 +777,7 @@ public class DeodexUtil {
private static RegisterType[][] mergeTable =
{
//Unknown Null Nonreference Reference Conflicted
//Unknown Null Nonreference Reference Conflicted
{Unknown, Null, NonReference, Reference, Conflicted}, //Unknown
{Null, Null, NonReference, Reference, Conflicted}, //Null
{NonReference, NonReference, NonReference, Conflicted, Conflicted}, //NonReference
@ -816,7 +814,7 @@ public class DeodexUtil {
public final SparseArray<insn> insnsMap;
/**
* Instructions that can execution could pass on to next
* Instructions that can execution could pass on to next
*/
public LinkedList<insn> successors = new LinkedList<insn>();
@ -827,7 +825,7 @@ public class DeodexUtil {
/**
* If this instruction is in a try block, these are the first instructions for each
* exception handler
* exception handler
*/
public insn[] exceptionHandlers = null;
@ -850,19 +848,19 @@ public class DeodexUtil {
public RegisterType registerType;
/**
* if setsRegister is true, and the register type is a reference, this is the
* reference type of the register, or null if not known yet.
* reference type of the register, or null if not known yet.
*/
public String registerReferenceType;
/**
* Stores a "fake" fixed instruction, which is included in the instruction list that deodexerizeCode produces
* Stores a "fake" fixed instruction, which is included in the instruction list that deodexerizeCode produces
*/
public Instruction fixedInstruction;
/**
* This is only used for odexed instructions, and should contain the register num of the object reference
* that the instruction acts on. More specifically, it's only for odexed instructions that require the
* type of the object register in order to look up the correct information.
* type of the object register in order to look up the correct information.
*/
public int objectRegisterNum = -1;
@ -969,32 +967,16 @@ public class DeodexUtil {
addSuccessor(getInstructionAtOffset(offset + ((OffsetInstruction)instruction).getOffset()));
break;
case PACKED_SWITCH:
case SPARSE_SWITCH:
{
insn packedSwitchDataInsn =
getInstructionAtOffset(offset + ((OffsetInstruction)instruction).getOffset());
assert packedSwitchDataInsn.instruction instanceof PackedSwitchDataPseudoInstruction;
PackedSwitchDataPseudoInstruction packedSwitchData =
(PackedSwitchDataPseudoInstruction)packedSwitchDataInsn.instruction;
Iterator<PackedSwitchDataPseudoInstruction.PackedSwitchTarget> iterator =
packedSwitchData.getTargets();
while (iterator.hasNext()) {
PackedSwitchDataPseudoInstruction.PackedSwitchTarget target = iterator.next();
addSuccessor(getInstructionAtOffset(offset + target.target));
}
break;
}
case SPARSE_SWITCH:
{
insn sparseSwitchDataInsn =
getInstructionAtOffset(offset + ((OffsetInstruction)instruction).getOffset());
assert sparseSwitchDataInsn.instruction instanceof SparseSwitchDataPseudoInstruction;
SparseSwitchDataPseudoInstruction sparseSwitchData =
(SparseSwitchDataPseudoInstruction)sparseSwitchDataInsn.instruction;
Iterator<SparseSwitchDataPseudoInstruction.SparseSwitchTarget> iterator =
sparseSwitchData.getTargets();
while (iterator.hasNext()) {
SparseSwitchDataPseudoInstruction.SparseSwitchTarget target = iterator.next();
addSuccessor(getInstructionAtOffset(offset + target.target));
assert packedSwitchDataInsn.instruction instanceof MultiOffsetInstruction;
MultiOffsetInstruction switchData =
(MultiOffsetInstruction)(packedSwitchDataInsn.instruction);
int[] packedSwitchTargets = switchData.getTargets();
for (int i=0; i<packedSwitchTargets.length; i++) {
addSuccessor(getInstructionAtOffset(offset + packedSwitchTargets[i]));
}
break;
}
@ -1146,7 +1128,7 @@ public class DeodexUtil {
//the array size for that case, but support the case of multiple exception types as well
List<String> exceptionTypes = new ArrayList<String>(1);
for (CodeItem.TryItem tryItem: codeItem.getTries()) {
if (tryItem.encodedCatchHandler.catchAllHandlerAddress == this.offset) {
if (tryItem.encodedCatchHandler.getCatchAllHandlerAddress() == this.offset) {
//if this is a catch all handler, the only possible type is Ljava/lang/Throwable;
registerReferenceType = "Ljava/lang/Throwable;";
@ -1160,7 +1142,7 @@ public class DeodexUtil {
}
for (CodeItem.EncodedTypeAddrPair handler: tryItem.encodedCatchHandler.handlers) {
if (handler.handlerAddress == this.offset) {
if (handler.getHandlerAddress() == this.offset) {
exceptionTypes.add(handler.exceptionType.getTypeDescriptor());
}
}
@ -1241,7 +1223,7 @@ public class DeodexUtil {
//if we got here, then we can assume that it's possible for execution to continue on to the next
//instruction. Otherwise, we would have returned from within the switch statement
addSuccessor(getInstructionAtOffset(offset + instruction.getSize()/2));
addSuccessor(getInstructionAtOffset(offset + instruction.getSize(offset)/2));
}
private String findCommonSuperclass(String type1, String type2) {
@ -1251,7 +1233,7 @@ public class DeodexUtil {
if (type2 == null) {
return type1;
}
if (type1.equals(type2)) {
return type1;
}
@ -1347,7 +1329,7 @@ public class DeodexUtil {
}
}
}
if (exceptionHandlers != null && canThrow) {
for (insn handlerinsn: exceptionHandlers) {
handlerinsn.initializeRegistersFromParams();
@ -1371,7 +1353,7 @@ public class DeodexUtil {
public void propogateDeadness() {
for (insn successor: successors) {
//the first instruction of the method (or the first instruction of any exception handlers covering
//the first instruction) can never be dead
//the first instruction) can never be dead
if (successor.firstInstruction) {
continue;
}
@ -1453,7 +1435,7 @@ public class DeodexUtil {
if (registerNum == nextInsn.objectRegisterNum) {
nextInsn.fixedInstruction = null;
}
somethingChanged = true;
nextInsn.registerTypes[registerNum] = registerReferenceType;
}
@ -1465,7 +1447,7 @@ public class DeodexUtil {
if (registerNum == nextInsn.objectRegisterNum) {
nextInsn.fixedInstruction = null;
}
somethingChanged = true;
nextInsn.registerTypes[registerNum] = type;
}

View File

@ -30,13 +30,13 @@ public final class Leb128Utils {
/**
* Gets the number of bytes in the unsigned LEB128 encoding of the
* given value.
*
*
* @param value the value in question
* @return its write size, in bytes
*/
public static int unsignedLeb128Size(int value) {
// TODO: This could be much cleverer.
int remaining = value >>> 7;
int count = 0;
@ -52,7 +52,7 @@ public final class Leb128Utils {
/**
* Gets the number of bytes in the signed LEB128 encoding of the
* given value.
*
*
* @param value the value in question
* @return its write size, in bytes
*/
@ -75,4 +75,25 @@ public final class Leb128Utils {
return count;
}
/**
* Writes an unsigned leb128 to the buffer at the specified location
* @param value the value to write as an unsigned leb128
* @param buffer the buffer to write to
* @param bufferIndex the index to start writing at
*/
public static void writeUnsignedLeb128(int value, byte[] buffer, int bufferIndex) {
int remaining = value >>> 7;
int count = 0;
while (remaining != 0) {
buffer[bufferIndex] = (byte)((value & 0x7f) | 0x80);
bufferIndex++;
value = remaining;
remaining >>>= 7;
count++;
}
buffer[bufferIndex] = (byte)(value & 0x7f);
}
}

View File

@ -0,0 +1,235 @@
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jf.dexlib.Util;
/**
* SparseIntArrays map integers to integers. Unlike a normal array of integers,
* there can be gaps in the indices. It is intended to be more efficient
* than using a HashMap to map Integers to Integers.
*/
public class SparseIntArray {
/**
* Creates a new SparseIntArray containing no mappings.
*/
public SparseIntArray() {
this(10);
}
/**
* Creates a new SparseIntArray containing no mappings that will not
* require any additional memory allocation to store the specified
* number of mappings.
*/
public SparseIntArray(int initialCapacity) {
mKeys = new int[initialCapacity];
mValues = new int[initialCapacity];
mSize = 0;
}
/**
* Gets the int mapped from the specified key, or <code>0</code>
* if no such mapping has been made.
*/
public int get(int key) {
return get(key, 0);
}
/**
* Gets the int mapped from the specified key, or the specified value
* if no such mapping has been made.
*/
public int get(int key, int valueIfKeyNotFound) {
int i = binarySearch(mKeys, 0, mSize, key);
if (i < 0) {
return valueIfKeyNotFound;
} else {
return mValues[i];
}
}
/**
* Removes the mapping from the specified key, if there was any.
*/
public void delete(int key) {
int i = binarySearch(mKeys, 0, mSize, key);
if (i >= 0) {
removeAt(i);
}
}
/**
* Removes the mapping at the given index.
*/
public void removeAt(int index) {
System.arraycopy(mKeys, index + 1, mKeys, index, mSize - (index + 1));
System.arraycopy(mValues, index + 1, mValues, index, mSize - (index + 1));
mSize--;
}
/**
* Adds a mapping from the specified key to the specified value,
* replacing the previous mapping from the specified key if there
* was one.
*/
public void put(int key, int value) {
int i = binarySearch(mKeys, 0, mSize, key);
if (i >= 0) {
mValues[i] = value;
} else {
i = ~i;
if (mSize >= mKeys.length) {
int n = Math.max(mSize + 1, mKeys.length * 2);
int[] nkeys = new int[n];
int[] nvalues = new int[n];
// Log.e("SparseIntArray", "grow " + mKeys.length + " to " + n);
System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
mKeys = nkeys;
mValues = nvalues;
}
if (mSize - i != 0) {
// Log.e("SparseIntArray", "move " + (mSize - i));
System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i);
System.arraycopy(mValues, i, mValues, i + 1, mSize - i);
}
mKeys[i] = key;
mValues[i] = value;
mSize++;
}
}
/**
* Returns the number of key-value mappings that this SparseIntArray
* currently stores.
*/
public int size() {
return mSize;
}
/**
* Given an index in the range <code>0...size()-1</code>, returns
* the key from the <code>index</code>th key-value mapping that this
* SparseIntArray stores.
*/
public int keyAt(int index) {
return mKeys[index];
}
/**
* Given an index in the range <code>0...size()-1</code>, returns
* the value from the <code>index</code>th key-value mapping that this
* SparseIntArray stores.
*/
public int valueAt(int index) {
return mValues[index];
}
/**
* Returns the index for which {@link #keyAt} would return the
* specified key, or a negative number if the specified
* key is not mapped.
*/
public int indexOfKey(int key) {
return binarySearch(mKeys, 0, mSize, key);
}
/**
* Returns an index for which {@link #valueAt} would return the
* specified key, or a negative number if no keys map to the
* specified value.
* Beware that this is a linear search, unlike lookups by key,
* and that multiple keys can map to the same value and this will
* find only one of them.
*/
public int indexOfValue(int value) {
for (int i = 0; i < mSize; i++)
if (mValues[i] == value)
return i;
return -1;
}
/**
* Removes all key-value mappings from this SparseIntArray.
*/
public void clear() {
mSize = 0;
}
/**
* Puts a key/value pair into the array, optimizing for the case where
* the key is greater than all existing keys in the array.
*/
public void append(int key, int value) {
if (mSize != 0 && key <= mKeys[mSize - 1]) {
put(key, value);
return;
}
int pos = mSize;
if (pos >= mKeys.length) {
int n = Math.max(pos + 1, mKeys.length * 2);
int[] nkeys = new int[n];
int[] nvalues = new int[n];
// Log.e("SparseIntArray", "grow " + mKeys.length + " to " + n);
System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
mKeys = nkeys;
mValues = nvalues;
}
mKeys[pos] = key;
mValues[pos] = value;
mSize = pos + 1;
}
private static int binarySearch(int[] a, int start, int len, int key) {
int high = start + len, low = start - 1, guess;
while (high - low > 1) {
guess = (high + low) / 2;
if (a[guess] < key)
low = guess;
else
high = guess;
}
if (high == start + len)
return ~(start + len);
else if (a[high] == key)
return high;
else
return ~high;
}
private int[] mKeys;
private int[] mValues;
private int mSize;
}

View File

@ -515,7 +515,7 @@ method returns[ ClassDataItem.EncodedMethod encodedMethod,
CodeItem codeItem;
if (totalMethodRegisters == 0 &&
$statements.encodedInstructions.length == 0 &&
$statements.instructions.size() == 0 &&
$method::labels.size()== 0 &&
(tries == null || tries.size() == 0) &&
(handlers == null || handlers.size() == 0) &&
@ -542,8 +542,7 @@ method returns[ ClassDataItem.EncodedMethod encodedMethod,
methodParameterRegisters,
$statements.maxOutRegisters,
debugInfoItem,
$statements.encodedInstructions,
$statements.referencedItems,
$statements.instructions,
tries,
handlers);
}
@ -793,23 +792,19 @@ source
$method::debugInfo.addSetFile($address.address, $string_literal.value);
};
statements[int totalMethodRegisters, int methodParameterRegisters] returns[byte[\] encodedInstructions, List<Item> referencedItems, int maxOutRegisters]
statements[int totalMethodRegisters, int methodParameterRegisters] returns[List<Instruction> instructions, int maxOutRegisters]
@init
{
ByteArrayOutput out = new ByteArrayOutput();
$referencedItems = new LinkedList<Item>();
$instructions = new LinkedList<Instruction>();
$maxOutRegisters = 0;
}
: ^(I_STATEMENTS (instruction[$totalMethodRegisters, $methodParameterRegisters, out, $referencedItems]
: ^(I_STATEMENTS (instruction[$totalMethodRegisters, $methodParameterRegisters, $instructions]
{
$method::currentAddress = out.getCursor() / 2;
$method::currentAddress += $instructions.get($instructions.size() - 1).getSize($method::currentAddress*2) / 2;
if ($maxOutRegisters < $instruction.outRegisters) {
$maxOutRegisters = $instruction.outRegisters;
}
})*)
{
$encodedInstructions = out.toByteArray();
};
})*);
label_ref returns[int labelAddress]
: label
@ -876,7 +871,7 @@ register_range[int totalMethodRegisters, int methodParameterRegisters] returns[i
}
;
instruction[int totalMethodRegisters, int methodParameterRegisters, Output out, List<Item> referencedItems] returns[int outRegisters]
instruction[int totalMethodRegisters, int methodParameterRegisters, List<Instruction> instructions] returns[int outRegisters]
: //e.g. goto endloop:
{$outRegisters = 0;}
^(I_STATEMENT_FORMAT10t INSTRUCTION_FORMAT10t offset_or_label)
@ -889,13 +884,13 @@ instruction[int totalMethodRegisters, int methodParameterRegisters, Output out,
throw new SemanticException(input, "The offset/label is out of range. The offset is " + Integer.toString(addressOffset) + " and the range for this opcode is [-128, 127].");
}
Instruction10t.emit(out, opcode, (byte)addressOffset);
$instructions.add(new Instruction10t(opcode, (byte)addressOffset));
}
| //e.g. return
^(I_STATEMENT_FORMAT10x INSTRUCTION_FORMAT10x)
{
Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT10x.text);
Instruction10x.emit(out, opcode);
$instructions.add(new Instruction10x(opcode));
}
| //e.g. const/4 v0, 5
^(I_STATEMENT_FORMAT11n INSTRUCTION_FORMAT11n REGISTER short_integral_literal)
@ -906,7 +901,7 @@ instruction[int totalMethodRegisters, int methodParameterRegisters, Output out,
short litB = $short_integral_literal.value;
literalTools.checkNibble(litB);
Instruction11n.emit(out, opcode, regA, (byte)litB);
$instructions.add(new Instruction11n(opcode, regA, (byte)litB));
}
| //e.g. move-result-object v1
^(I_STATEMENT_FORMAT11x INSTRUCTION_FORMAT11x REGISTER)
@ -914,7 +909,7 @@ instruction[int totalMethodRegisters, int methodParameterRegisters, Output out,
Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT11x.text);
short regA = parseRegister_byte($REGISTER.text, $totalMethodRegisters, $methodParameterRegisters);
Instruction11x.emit(out, opcode, regA);
$instructions.add(new Instruction11x(opcode, regA));
}
| //e.g. move v1 v2
^(I_STATEMENT_FORMAT12x INSTRUCTION_FORMAT12x registerA=REGISTER registerB=REGISTER)
@ -923,7 +918,7 @@ instruction[int totalMethodRegisters, int methodParameterRegisters, Output out,
byte regA = parseRegister_nibble($registerA.text, $totalMethodRegisters, $methodParameterRegisters);
byte regB = parseRegister_nibble($registerB.text, $totalMethodRegisters, $methodParameterRegisters);
Instruction12x.emit(out, opcode, regA, regB);
$instructions.add(new Instruction12x(opcode, regA, regB));
}
| //e.g. goto/16 endloop:
^(I_STATEMENT_FORMAT20t INSTRUCTION_FORMAT20t offset_or_label)
@ -936,7 +931,7 @@ instruction[int totalMethodRegisters, int methodParameterRegisters, Output out,
throw new SemanticException(input, "The offset/label is out of range. The offset is " + Integer.toString(addressOffset) + " and the range for this opcode is [-32768, 32767].");
}
Instruction20t.emit(out, opcode, (short)addressOffset);
$instructions.add(new Instruction20t(opcode, (short)addressOffset));
}
| //e.g. sget_object v0 java/lang/System/out LJava/io/PrintStream;
^(I_STATEMENT_FORMAT21c_FIELD INSTRUCTION_FORMAT21c_FIELD REGISTER fully_qualified_field)
@ -946,8 +941,7 @@ instruction[int totalMethodRegisters, int methodParameterRegisters, Output out,
FieldIdItem fieldIdItem = $fully_qualified_field.fieldIdItem;
Instruction21c.emit(out, opcode, regA, fieldIdItem);
$referencedItems.add(fieldIdItem);
$instructions.add(new Instruction21c(opcode, regA, fieldIdItem));
}
| //e.g. const-string v1 "Hello World!"
^(I_STATEMENT_FORMAT21c_STRING INSTRUCTION_FORMAT21c_STRING REGISTER string_literal)
@ -957,8 +951,7 @@ instruction[int totalMethodRegisters, int methodParameterRegisters, Output out,
StringIdItem stringIdItem = StringIdItem.getInternedStringIdItem(dexFile, $string_literal.value);
Instruction21c.emit(out, opcode, regA, stringIdItem);
$referencedItems.add(stringIdItem);
instructions.add(new Instruction21c(opcode, regA, stringIdItem));
}
| //e.g. const-class v2 org/jf/HelloWorld2/HelloWorld2
^(I_STATEMENT_FORMAT21c_TYPE INSTRUCTION_FORMAT21c_TYPE REGISTER reference_type_descriptor)
@ -968,8 +961,7 @@ instruction[int totalMethodRegisters, int methodParameterRegisters, Output out,
TypeIdItem typeIdItem = $reference_type_descriptor.type;
Instruction21c.emit(out, opcode, regA, typeIdItem);
$referencedItems.add(typeIdItem);
$instructions.add(new Instruction21c(opcode, regA, typeIdItem));
}
| //e.g. const/high16 v1, 1234
^(I_STATEMENT_FORMAT21h INSTRUCTION_FORMAT21h REGISTER short_integral_literal)
@ -979,7 +971,7 @@ instruction[int totalMethodRegisters, int methodParameterRegisters, Output out,
short litB = $short_integral_literal.value;
Instruction21h.emit(out, opcode, regA, litB);
instructions.add(new Instruction21h(opcode, regA, litB));
}
| //e.g. const/16 v1, 1234
^(I_STATEMENT_FORMAT21s INSTRUCTION_FORMAT21s REGISTER short_integral_literal)
@ -989,7 +981,7 @@ instruction[int totalMethodRegisters, int methodParameterRegisters, Output out,
short litB = $short_integral_literal.value;
Instruction21s.emit(out, opcode, regA, litB);
$instructions.add(new Instruction21s(opcode, regA, litB));
}
| //e.g. if-eqz v0, endloop:
^(I_STATEMENT_FORMAT21t INSTRUCTION_FORMAT21t REGISTER offset_or_label)
@ -1003,7 +995,7 @@ instruction[int totalMethodRegisters, int methodParameterRegisters, Output out,
throw new SemanticException(input, "The offset/label is out of range. The offset is " + Integer.toString(addressOffset) + " and the range for this opcode is [-32768, 32767].");
}
Instruction21t.emit(out, opcode, regA, (short)addressOffset);
$instructions.add(new Instruction21t(opcode, regA, (short)addressOffset));
}
| //e.g. add-int v0, v1, 123
^(I_STATEMENT_FORMAT22b INSTRUCTION_FORMAT22b registerA=REGISTER registerB=REGISTER short_integral_literal)
@ -1015,7 +1007,7 @@ instruction[int totalMethodRegisters, int methodParameterRegisters, Output out,
short litC = $short_integral_literal.value;
literalTools.checkByte(litC);
Instruction22b.emit(out, opcode, regA, regB, (byte)litC);
$instructions.add(new Instruction22b(opcode, regA, regB, (byte)litC));
}
| //e.g. iput-object v1 v0 org/jf/HelloWorld2/HelloWorld2.helloWorld Ljava/lang/String;
^(I_STATEMENT_FORMAT22c_FIELD INSTRUCTION_FORMAT22c_FIELD registerA=REGISTER registerB=REGISTER fully_qualified_field)
@ -1026,8 +1018,7 @@ instruction[int totalMethodRegisters, int methodParameterRegisters, Output out,
FieldIdItem fieldIdItem = $fully_qualified_field.fieldIdItem;
Instruction22c.emit(out, opcode, regA, regB);
$referencedItems.add(fieldIdItem);
$instructions.add(new Instruction22c(opcode, regA, regB, fieldIdItem));
}
| //e.g. instance-of v0, v1, Ljava/lang/String;
^(I_STATEMENT_FORMAT22c_TYPE INSTRUCTION_FORMAT22c_TYPE registerA=REGISTER registerB=REGISTER nonvoid_type_descriptor)
@ -1038,8 +1029,7 @@ instruction[int totalMethodRegisters, int methodParameterRegisters, Output out,
TypeIdItem typeIdItem = $nonvoid_type_descriptor.type;
Instruction22c.emit(out, opcode, regA, regB);
$referencedItems.add(typeIdItem);
$instructions.add(new Instruction22c(opcode, regA, regB, typeIdItem));
}
| //e.g. add-int/lit16 v0, v1, 12345
^(I_STATEMENT_FORMAT22s INSTRUCTION_FORMAT22s registerA=REGISTER registerB=REGISTER short_integral_literal)
@ -1050,7 +1040,7 @@ instruction[int totalMethodRegisters, int methodParameterRegisters, Output out,
short litC = $short_integral_literal.value;
Instruction22s.emit(out, opcode, regA, regB, litC);
$instructions.add(new Instruction22s(opcode, regA, regB, litC));
}
| //e.g. if-eq v0, v1, endloop:
^(I_STATEMENT_FORMAT22t INSTRUCTION_FORMAT22t registerA=REGISTER registerB=REGISTER offset_or_label)
@ -1065,7 +1055,7 @@ instruction[int totalMethodRegisters, int methodParameterRegisters, Output out,
throw new SemanticException(input, "The offset/label is out of range. The offset is " + Integer.toString(addressOffset) + " and the range for this opcode is [-32768, 32767].");
}
Instruction22t.emit(out, opcode, regA, regB, (short)addressOffset);
$instructions.add(new Instruction22t(opcode, regA, regB, (short)addressOffset));
}
| //e.g. move/from16 v1, v1234
^(I_STATEMENT_FORMAT22x INSTRUCTION_FORMAT22x registerA=REGISTER registerB=REGISTER)
@ -1074,7 +1064,7 @@ instruction[int totalMethodRegisters, int methodParameterRegisters, Output out,
short regA = parseRegister_byte($registerA.text, $totalMethodRegisters, $methodParameterRegisters);
int regB = parseRegister_short($registerB.text, $totalMethodRegisters, $methodParameterRegisters);
Instruction22x.emit(out, opcode, regA, regB);
$instructions.add(new Instruction22x(opcode, regA, regB));
}
| //e.g. add-int v1, v2, v3
^(I_STATEMENT_FORMAT23x INSTRUCTION_FORMAT23x registerA=REGISTER registerB=REGISTER registerC=REGISTER)
@ -1084,7 +1074,7 @@ instruction[int totalMethodRegisters, int methodParameterRegisters, Output out,
short regB = parseRegister_byte($registerB.text, $totalMethodRegisters, $methodParameterRegisters);
short regC = parseRegister_byte($registerC.text, $totalMethodRegisters, $methodParameterRegisters);
Instruction23x.emit(out, opcode, regA, regB, regC);
$instructions.add(new Instruction23x(opcode, regA, regB, regC));
}
| //e.g. goto/32 endloop:
^(I_STATEMENT_FORMAT30t INSTRUCTION_FORMAT30t offset_or_label)
@ -1093,7 +1083,7 @@ instruction[int totalMethodRegisters, int methodParameterRegisters, Output out,
int addressOffset = $offset_or_label.offsetValue;
Instruction30t.emit(out, opcode, addressOffset);
$instructions.add(new Instruction30t(opcode, addressOffset));
}
| //e.g. const-string/jumbo v1 "Hello World!"
^(I_STATEMENT_FORMAT31c INSTRUCTION_FORMAT31c REGISTER string_literal)
@ -1103,8 +1093,7 @@ instruction[int totalMethodRegisters, int methodParameterRegisters, Output out,
StringIdItem stringIdItem = StringIdItem.getInternedStringIdItem(dexFile, $string_literal.value);
Instruction31c.emit(out, opcode, regA);
$referencedItems.add(stringIdItem);
$instructions.add(new Instruction31c(opcode, regA, stringIdItem));
}
| //e.g. const v0, 123456
^(I_STATEMENT_FORMAT31i INSTRUCTION_FORMAT31i REGISTER fixed_32bit_literal)
@ -1114,7 +1103,7 @@ instruction[int totalMethodRegisters, int methodParameterRegisters, Output out,
int litB = $fixed_32bit_literal.value;
Instruction31i.emit(out, opcode, regA, litB);
$instructions.add(new Instruction31i(opcode, regA, litB));
}
| //e.g. fill-array-data v0, ArrayData:
^(I_STATEMENT_FORMAT31t INSTRUCTION_FORMAT31t REGISTER offset_or_label)
@ -1128,7 +1117,7 @@ instruction[int totalMethodRegisters, int methodParameterRegisters, Output out,
addressOffset++;
}
Instruction31t.emit(out, opcode, regA, addressOffset);
$instructions.add(new Instruction31t(opcode, regA, addressOffset));
}
| //e.g. move/16 v5678, v1234
^(I_STATEMENT_FORMAT32x INSTRUCTION_FORMAT32x registerA=REGISTER registerB=REGISTER)
@ -1137,7 +1126,7 @@ instruction[int totalMethodRegisters, int methodParameterRegisters, Output out,
int regA = parseRegister_short($registerA.text, $totalMethodRegisters, $methodParameterRegisters);
int regB = parseRegister_short($registerB.text, $totalMethodRegisters, $methodParameterRegisters);
Instruction32x.emit(out, opcode, regA, regB);
$instructions.add(new Instruction32x(opcode, regA, regB));
}
| //e.g. invoke-virtual {v0,v1} java/io/PrintStream/print(Ljava/lang/Stream;)V
^(I_STATEMENT_FORMAT35c_METHOD INSTRUCTION_FORMAT35c_METHOD register_list[$totalMethodRegisters, $methodParameterRegisters] fully_qualified_method)
@ -1151,8 +1140,7 @@ instruction[int totalMethodRegisters, int methodParameterRegisters, Output out,
MethodIdItem methodIdItem = $fully_qualified_method.methodIdItem;
Instruction35c.emit(out, opcode, registerCount, registers[0], registers[1], registers[2], registers[3], registers[4], methodIdItem);
$referencedItems.add(methodIdItem);
$instructions.add(new Instruction35c(opcode, registerCount, registers[0], registers[1], registers[2], registers[3], registers[4], methodIdItem));
}
| //e.g. filled-new-array {v0,v1}, I
^(I_STATEMENT_FORMAT35c_TYPE INSTRUCTION_FORMAT35c_TYPE register_list[$totalMethodRegisters, $methodParameterRegisters] nonvoid_type_descriptor)
@ -1166,8 +1154,7 @@ instruction[int totalMethodRegisters, int methodParameterRegisters, Output out,
TypeIdItem typeIdItem = $nonvoid_type_descriptor.type;
Instruction35c.emit(out, opcode, registerCount, registers[0], registers[1], registers[2], registers[3], registers[4], typeIdItem);
$referencedItems.add(typeIdItem);
$instructions.add(new Instruction35c(opcode, registerCount, registers[0], registers[1], registers[2], registers[3], registers[4], typeIdItem));
}
| //e.g. invoke-virtual/range {v25..v26} java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder;
^(I_STATEMENT_FORMAT3rc_METHOD INSTRUCTION_FORMAT3rc_METHOD register_range[$totalMethodRegisters, $methodParameterRegisters] fully_qualified_method)
@ -1188,8 +1175,7 @@ instruction[int totalMethodRegisters, int methodParameterRegisters, Output out,
MethodIdItem methodIdItem = $fully_qualified_method.methodIdItem;
Instruction3rc.emit(out, opcode, (short)registerCount, startRegister, methodIdItem);
$referencedItems.add(methodIdItem);
$instructions.add(new Instruction3rc(opcode, (short)registerCount, startRegister, methodIdItem));
}
| //e.g. filled-new-array/range {v0..v6} I
^(I_STATEMENT_FORMAT3rc_TYPE INSTRUCTION_FORMAT3rc_TYPE register_range[$totalMethodRegisters, $methodParameterRegisters] nonvoid_type_descriptor)
@ -1210,8 +1196,7 @@ instruction[int totalMethodRegisters, int methodParameterRegisters, Output out,
TypeIdItem typeIdItem = $nonvoid_type_descriptor.type;
Instruction3rc.emit(out, opcode, (short)registerCount, startRegister, typeIdItem);
$referencedItems.add(typeIdItem);
$instructions.add(new Instruction3rc(opcode, (short)registerCount, startRegister, typeIdItem));
}
| //e.g. const-wide v0, 5000000000L
^(I_STATEMENT_FORMAT51l INSTRUCTION_FORMAT51l REGISTER fixed_64bit_literal)
@ -1221,12 +1206,15 @@ instruction[int totalMethodRegisters, int methodParameterRegisters, Output out,
long litB = $fixed_64bit_literal.value;
Instruction51l.emit(out, opcode, regA, litB);
$instructions.add(new Instruction51l(opcode, regA, litB));
}
| //e.g. .array-data 4 1000000 .end array-data
^(I_STATEMENT_ARRAY_DATA ^(I_ARRAY_ELEMENT_SIZE short_integral_literal) array_elements)
{
out.alignTo(4);
if (($method::currentAddress \% 2) != 0) {
$instructions.add(new Instruction10x(Opcode.NOP));
$method::currentAddress++;
}
int elementWidth = $short_integral_literal.value;
List<byte[]> byteValues = $array_elements.values;
@ -1243,14 +1231,16 @@ instruction[int totalMethodRegisters, int methodParameterRegisters, Output out,
index+=byteValue.length;
}
ArrayDataPseudoInstruction.emit(out, elementWidth, encodedValues);
$instructions.add(new ArrayDataPseudoInstruction(elementWidth, encodedValues));
}
|
^(I_STATEMENT_PACKED_SWITCH ^(I_PACKED_SWITCH_START_KEY fixed_32bit_literal)
{
out.alignTo(4);
$method::currentAddress = out.getCursor() / 2;
if (($method::currentAddress \% 2) != 0) {
$instructions.add(new Instruction10x(Opcode.NOP));
$method::currentAddress++;
}
Integer baseAddress = $method::packedSwitchDeclarations.get($method::currentAddress);
if (baseAddress == null) {
baseAddress = 0;
@ -1262,13 +1252,15 @@ instruction[int totalMethodRegisters, int methodParameterRegisters, Output out,
int startKey = $fixed_32bit_literal.value;
int[] targets = $packed_switch_targets.targets;
PackedSwitchDataPseudoInstruction.emit(out, startKey, targets);
$instructions.add(new PackedSwitchDataPseudoInstruction(startKey, targets));
}
|
^(I_STATEMENT_SPARSE_SWITCH sparse_switch_target_count sparse_switch_keys[$sparse_switch_target_count.targetCount]
{
out.alignTo(4);
$method::currentAddress = out.getCursor() / 2;
if (($method::currentAddress \% 2) != 0) {
$instructions.add(new Instruction10x(Opcode.NOP));
$method::currentAddress++;
}
Integer baseAddress = $method::sparseSwitchDeclarations.get($method::currentAddress);
if (baseAddress == null) {
baseAddress = 0;
@ -1280,7 +1272,7 @@ instruction[int totalMethodRegisters, int methodParameterRegisters, Output out,
int[] keys = $sparse_switch_keys.keys;
int[] targets = $sparse_switch_targets.targets;
SparseSwitchDataPseudoInstruction.emit(out, keys, targets);
$instructions.add(new SparseSwitchDataPseudoInstruction(keys, targets));
};
catch [Exception ex] {
reportError(new SemanticException(input, ex));