mirror of
https://github.com/revanced/smali.git
synced 2025-05-28 03:40:12 +02:00
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:
parent
9ab2b45ec8
commit
fda2e631ac
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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_");
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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) ::=
|
||||
|
@ -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;
|
||||
|
@ -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() {
|
||||
|
@ -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 {
|
||||
|
@ -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() {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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);
|
||||
}
|
@ -30,4 +30,5 @@ package org.jf.dexlib.Code;
|
||||
|
||||
public interface OffsetInstruction {
|
||||
public int getOffset();
|
||||
public void updateOffset(int offset);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -30,8 +30,6 @@ package org.jf.dexlib;
|
||||
|
||||
import org.jf.dexlib.Util.Input;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
public class OdexHeaderItem {
|
||||
|
||||
/**
|
||||
|
@ -111,7 +111,7 @@ public class ByteArrayInput
|
||||
}
|
||||
|
||||
cursor = end;
|
||||
return (int)((data[readAt] & 0xff) +
|
||||
return ((data[readAt] & 0xff) +
|
||||
((data[readAt + 1] & 0xff) << 8));
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
235
dexlib/src/main/java/org/jf/dexlib/Util/SparseIntArray.java
Normal file
235
dexlib/src/main/java/org/jf/dexlib/Util/SparseIntArray.java
Normal 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;
|
||||
}
|
@ -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));
|
||||
|
Loading…
x
Reference in New Issue
Block a user