From fda2e631ac0b1ca092973b7fff4b2f38d2c23437 Mon Sep 17 00:00:00 2001 From: "JesusFreke@JesusFreke.com" Date: Wed, 23 Dec 2009 05:26:59 +0000 Subject: [PATCH] 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 --- .../jf/baksmali/Adaptors/ClassDefinition.java | 47 +- .../Format/Instruction31tMethodItem.java | 2 +- .../Format/PackedSwitchMethodItem.java | 2 +- .../Format/SparseSwitchMethodItem.java | 7 +- .../baksmali/Adaptors/MethodDefinition.java | 62 +-- .../templates/templates/baksmali.stg | 6 +- .../Format/ArrayDataPseudoInstruction.java | 61 ++- .../dexlib/Code/Format/DeadInstruction.java | 9 +- .../jf/dexlib/Code/Format/Instruction10t.java | 40 +- .../jf/dexlib/Code/Format/Instruction10x.java | 19 +- .../jf/dexlib/Code/Format/Instruction11n.java | 26 +- .../jf/dexlib/Code/Format/Instruction11x.java | 21 +- .../jf/dexlib/Code/Format/Instruction12x.java | 25 +- .../jf/dexlib/Code/Format/Instruction20t.java | 46 +- .../jf/dexlib/Code/Format/Instruction21c.java | 22 +- .../jf/dexlib/Code/Format/Instruction21h.java | 28 +- .../jf/dexlib/Code/Format/Instruction21s.java | 28 +- .../jf/dexlib/Code/Format/Instruction21t.java | 41 +- .../jf/dexlib/Code/Format/Instruction22b.java | 34 +- .../jf/dexlib/Code/Format/Instruction22c.java | 27 +- .../dexlib/Code/Format/Instruction22cs.java | 32 +- .../dexlib/Code/Format/Instruction22csf.java | 10 + .../jf/dexlib/Code/Format/Instruction22s.java | 33 +- .../jf/dexlib/Code/Format/Instruction22t.java | 58 ++- .../jf/dexlib/Code/Format/Instruction22x.java | 28 +- .../jf/dexlib/Code/Format/Instruction23x.java | 34 +- .../jf/dexlib/Code/Format/Instruction30t.java | 32 +- .../jf/dexlib/Code/Format/Instruction31c.java | 25 +- .../jf/dexlib/Code/Format/Instruction31i.java | 28 +- .../jf/dexlib/Code/Format/Instruction31t.java | 38 +- .../jf/dexlib/Code/Format/Instruction32x.java | 28 +- .../jf/dexlib/Code/Format/Instruction35c.java | 52 ++- .../dexlib/Code/Format/Instruction35ms.java | 52 ++- .../dexlib/Code/Format/Instruction35msf.java | 18 +- .../jf/dexlib/Code/Format/Instruction35s.java | 112 ++++- .../dexlib/Code/Format/Instruction35sf.java | 16 + .../jf/dexlib/Code/Format/Instruction3rc.java | 28 +- .../dexlib/Code/Format/Instruction3rms.java | 37 +- .../dexlib/Code/Format/Instruction3rmsf.java | 11 + .../jf/dexlib/Code/Format/Instruction51l.java | 28 +- .../PackedSwitchDataPseudoInstruction.java | 76 +++- .../SparseSwitchDataPseudoInstruction.java | 88 ++-- .../Code/Format/UnresolvedNullReference.java | 14 +- .../java/org/jf/dexlib/Code/Instruction.java | 42 +- .../jf/dexlib/Code/InstructionIterator.java | 29 +- .../dexlib/Code/InstructionWithReference.java | 8 +- .../dexlib/Code/MultiOffsetInstruction.java | 34 ++ .../org/jf/dexlib/Code/OffsetInstruction.java | 1 + .../src/main/java/org/jf/dexlib/CodeItem.java | 412 ++++++++++++++---- .../java/org/jf/dexlib/DebugInfoItem.java | 11 + .../src/main/java/org/jf/dexlib/DexFile.java | 21 +- .../main/java/org/jf/dexlib/HeaderItem.java | 2 - .../java/org/jf/dexlib/OdexHeaderItem.java | 2 - .../org/jf/dexlib/Util/ByteArrayInput.java | 2 +- .../org/jf/dexlib/Util/DebugInfoBuilder.java | 8 +- .../java/org/jf/dexlib/Util/DeodexUtil.java | 114 ++--- .../java/org/jf/dexlib/Util/Leb128Utils.java | 27 +- .../org/jf/dexlib/Util/SparseIntArray.java | 235 ++++++++++ .../antlr3/org/jf/smali/smaliTreeWalker.g | 112 +++-- 59 files changed, 1810 insertions(+), 681 deletions(-) create mode 100644 dexlib/src/main/java/org/jf/dexlib/Code/MultiOffsetInstruction.java create mode 100644 dexlib/src/main/java/org/jf/dexlib/Util/SparseIntArray.java diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/ClassDefinition.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/ClassDefinition.java index 4b5f14b9..8003acd1 100644 --- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/ClassDefinition.java +++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/ClassDefinition.java @@ -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("")) { - 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); + } + } } } } diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/Instruction31tMethodItem.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/Instruction31tMethodItem.java index 06662e3b..ce006d30 100644 --- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/Instruction31tMethodItem.java +++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/Instruction31tMethodItem.java @@ -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() { diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/PackedSwitchMethodItem.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/PackedSwitchMethodItem.java index 464ed4da..893ffb24 100644 --- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/PackedSwitchMethodItem.java +++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/PackedSwitchMethodItem.java @@ -48,7 +48,7 @@ public class PackedSwitchMethodItem extends InstructionFormatMethodItem iterator = instruction.getTargets(); + Iterator iterator = instruction.iterateKeysAndTargets(); while (iterator.hasNext()) { PackedSwitchDataPseudoInstruction.PackedSwitchTarget target = iterator.next(); LabelMethodItem label = new LabelMethodItem(baseAddress + target.target, stg, "pswitch_"); diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/SparseSwitchMethodItem.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/SparseSwitchMethodItem.java index f57e4b17..07ce6a10 100644 --- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/SparseSwitchMethodItem.java +++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/SparseSwitchMethodItem.java @@ -48,11 +48,12 @@ public class SparseSwitchMethodItem extends InstructionFormatMethodItem iterator = instruction.getTargets(); + + Iterator 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 @@ -324,7 +324,7 @@ PackedSwitchData(Opcode, FirstKey, Targets) ::= SparseSwitchData(Opcode, Targets) ::= << .sparse-switch - -> }; separator="\n"> + -> }; separator="\n"> .end sparse-switch >> @@ -366,7 +366,7 @@ RestartLocal(Register, Name, Type, Signature) ::= SetFile(FileName) ::= << -.source "" +.source "" >> Blank(Blank) ::= diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/ArrayDataPseudoInstruction.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/ArrayDataPseudoInstruction.java index 96847d76..384c5ffa 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/ArrayDataPseudoInstruction.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/ArrayDataPseudoInstruction.java @@ -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() { 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 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 { diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction10x.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction10x.java index 30ee5867..50304852 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction10x.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction10x.java @@ -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() { diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction11n.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction11n.java index 50f3d595..074a6712 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction11n.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction11n.java @@ -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 { diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction11x.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction11x.java index df874dba..354e1b53 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction11x.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction11x.java @@ -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 { diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction12x.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction12x.java index 299312d3..d3192ad0 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction12x.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction12x.java @@ -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 { diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction20t.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction20t.java index 6168bc19..da4f2c35 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction20t.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction20t.java @@ -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); } diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21c.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21c.java index e4bbcc30..20c4fa64 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21c.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21c.java @@ -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 { diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21h.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21h.java index 1f87d878..a509874e 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21h.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21h.java @@ -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 { diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21s.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21s.java index e37aefd9..13a73d32 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21s.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21s.java @@ -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 { diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21t.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21t.java index 83288859..f0dd11aa 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21t.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction21t.java @@ -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 { diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22b.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22b.java index a7d19b07..54277644 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22b.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22b.java @@ -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 { diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22c.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22c.java index 37019c56..95f3a3da 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22c.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22c.java @@ -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 { diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22cs.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22cs.java index 4f512102..d415497b 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22cs.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22cs.java @@ -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 { diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22csf.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22csf.java index ca5eb8e9..9d6677dc 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22csf.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22csf.java @@ -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; } diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22s.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22s.java index 8c7c8176..17262b1f 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22s.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22s.java @@ -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 { diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22t.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22t.java index 241039f0..7fa8372e 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22t.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22t.java @@ -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); } } -} +} \ No newline at end of file diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22x.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22x.java index 77bd4b20..4571a426 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22x.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction22x.java @@ -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 { diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction23x.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction23x.java index addb9fad..af5683e9 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction23x.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction23x.java @@ -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 { diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction30t.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction30t.java index d7ba1f27..805dc714 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction30t.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction30t.java @@ -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); } diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31c.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31c.java index 0f3a05d5..8ec96412 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31c.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31c.java @@ -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 { diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31i.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31i.java index c15695e5..71913544 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31i.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31i.java @@ -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 { diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31t.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31t.java index f4c297a3..4242f187 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31t.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction31t.java @@ -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 { diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction32x.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction32x.java index e09ea642..f8ef53e7 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction32x.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction32x.java @@ -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 { diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35c.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35c.java index b8dddbf6..2ea0552c 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35c.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35c.java @@ -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); } } -} +} \ No newline at end of file diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35ms.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35ms.java index fdad8883..1f247cf2 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35ms.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35ms.java @@ -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 { diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35msf.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35msf.java index b029c546..5374d3f4 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35msf.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35msf.java @@ -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(); } diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35s.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35s.java index 47770cb6..35277e3c 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35s.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35s.java @@ -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 { diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35sf.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35sf.java index 785babca..7480ed05 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35sf.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction35sf.java @@ -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; } diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rc.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rc.java index 5975bc49..eacf241c 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rc.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rc.java @@ -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) { diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rms.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rms.java index b7bf031f..b26d5eef 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rms.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rms.java @@ -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) { diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rmsf.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rmsf.java index 380d25df..9081c57a 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rmsf.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rmsf.java @@ -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; } diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction51l.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction51l.java index a3f06ed5..8040df47 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction51l.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction51l.java @@ -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 { diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/PackedSwitchDataPseudoInstruction.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/PackedSwitchDataPseudoInstruction.java index eabccf87..7032925f 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/PackedSwitchDataPseudoInstruction.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/PackedSwitchDataPseudoInstruction.java @@ -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 getTargets() { + public Iterator iterateKeysAndTargets() { return new Iterator() { 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; } diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/SparseSwitchDataPseudoInstruction.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/SparseSwitchDataPseudoInstruction.java index 4fb27932..04158a98 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/SparseSwitchDataPseudoInstruction.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/SparseSwitchDataPseudoInstruction.java @@ -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 getTargets() { + public Iterator iterateKeysAndTargets() { return new Iterator() { 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; } diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/UnresolvedNullReference.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/UnresolvedNullReference.java index 8d71b253..3c21e211 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/UnresolvedNullReference.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/UnresolvedNullReference.java @@ -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() { diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Instruction.java b/dexlib/src/main/java/org/jf/dexlib/Code/Instruction.java index b44742e6..b9f8994d 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Instruction.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Instruction.java @@ -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); } diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/InstructionIterator.java b/dexlib/src/main/java/org/jf/dexlib/Code/InstructionIterator.java index dac65615..f7e5e6fb 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/InstructionIterator.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/InstructionIterator.java @@ -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); } } diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/InstructionWithReference.java b/dexlib/src/main/java/org/jf/dexlib/Code/InstructionWithReference.java index 63652a9e..6624195b 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/InstructionWithReference.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/InstructionWithReference.java @@ -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() { diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/MultiOffsetInstruction.java b/dexlib/src/main/java/org/jf/dexlib/Code/MultiOffsetInstruction.java new file mode 100644 index 00000000..94ce03df --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Code/MultiOffsetInstruction.java @@ -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); +} diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/OffsetInstruction.java b/dexlib/src/main/java/org/jf/dexlib/Code/OffsetInstruction.java index 0655245b..114223a9 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/OffsetInstruction.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/OffsetInstruction.java @@ -30,4 +30,5 @@ package org.jf.dexlib.Code; public interface OffsetInstruction { public int getOffset(); + public void updateOffset(int offset); } diff --git a/dexlib/src/main/java/org/jf/dexlib/CodeItem.java b/dexlib/src/main/java/org/jf/dexlib/CodeItem.java index 9e04d8ba..ba2ec62f 100644 --- a/dexlib/src/main/java/org/jf/dexlib/CodeItem.java +++ b/dexlib/src/main/java/org/jf/dexlib/CodeItem.java @@ -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 { 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 { * @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 { 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 { 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 { * @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 CodeItem with the given values. @@ -112,18 +107,12 @@ public class CodeItem extends Item { int inWords, int outWords, DebugInfoItem debugInfo, - byte[] encodedInstructions, - List referencedItems, + List instructions, List tries, List 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 { 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 { this.debugInfo.setParent(this); } int instructionCount = in.readInt(); - this.encodedInstructions = in.readBytes(instructionCount * 2); - this.referencedItems = InstructionReader.getReferencedItems(encodedInstructions, dexFile); + + final ArrayList instructionList = new ArrayList(); + + 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 { /** {@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 { /** {@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 { } 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 { } 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 { out.deindent(); } } else { - if ((encodedInstructions.length % 4) != 0) { + if ((currentCodeOffset % 4) != 0) { out.writeShort(0); } @@ -343,10 +333,10 @@ public class CodeItem extends Item { } /** - * @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 { /** * Used by OdexUtil to update this CodeItem 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 referencedItemsList = new LinkedList(); + 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= 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= 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 { * 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 { } } + /** + * Returns the "Catch All" handler address for this EncodedCatchHandler + * @return + */ + public int getCatchAllHandlerAddress() { + return catchAllHandlerAddress; + } + /** * @return the offset of this EncodedCatchHandler from the beginning of the * encoded_catch_handler_list structure @@ -643,7 +885,7 @@ public class CodeItem extends Item { /** * The address (in 2-byte words) in the code of the handler */ - public final int handlerAddress; + private int handlerAddress; /** * Constructs a new EncodedTypeAddrPair with the given values @@ -691,6 +933,10 @@ public class CodeItem extends Item { } } + public int getHandlerAddress() { + return handlerAddress; + } + @Override public int hashCode() { return exceptionType.hashCode() * 31 + handlerAddress; diff --git a/dexlib/src/main/java/org/jf/dexlib/DebugInfoItem.java b/dexlib/src/main/java/org/jf/dexlib/DebugInfoItem.java index e4b55a46..5a7249fa 100644 --- a/dexlib/src/main/java/org/jf/dexlib/DebugInfoItem.java +++ b/dexlib/src/main/java/org/jf/dexlib/DebugInfoItem.java @@ -269,6 +269,17 @@ public class DebugInfoItem extends Item { } } + /** + * 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 diff --git a/dexlib/src/main/java/org/jf/dexlib/DexFile.java b/dexlib/src/main/java/org/jf/dexlib/DexFile.java index d3860237..c2de17df 100644 --- a/dexlib/src/main/java/org/jf/dexlib/DexFile.java +++ b/dexlib/src/main/java/org/jf/dexlib/DexFile.java @@ -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; } diff --git a/dexlib/src/main/java/org/jf/dexlib/HeaderItem.java b/dexlib/src/main/java/org/jf/dexlib/HeaderItem.java index b7d57889..0e5354aa 100644 --- a/dexlib/src/main/java/org/jf/dexlib/HeaderItem.java +++ b/dexlib/src/main/java/org/jf/dexlib/HeaderItem.java @@ -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 { /** * the file format magic number, represented as the diff --git a/dexlib/src/main/java/org/jf/dexlib/OdexHeaderItem.java b/dexlib/src/main/java/org/jf/dexlib/OdexHeaderItem.java index b20a8ec5..7b5330ad 100644 --- a/dexlib/src/main/java/org/jf/dexlib/OdexHeaderItem.java +++ b/dexlib/src/main/java/org/jf/dexlib/OdexHeaderItem.java @@ -30,8 +30,6 @@ package org.jf.dexlib; import org.jf.dexlib.Util.Input; -import java.io.UnsupportedEncodingException; - public class OdexHeaderItem { /** diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayInput.java b/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayInput.java index 8ebab80c..775ac74d 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayInput.java +++ b/dexlib/src/main/java/org/jf/dexlib/Util/ByteArrayInput.java @@ -111,7 +111,7 @@ public class ByteArrayInput } cursor = end; - return (int)((data[readAt] & 0xff) + + return ((data[readAt] & 0xff) + ((data[readAt + 1] & 0xff) << 8)); } diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/DebugInfoBuilder.java b/dexlib/src/main/java/org/jf/dexlib/Util/DebugInfoBuilder.java index b2176bb7..a0716450 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Util/DebugInfoBuilder.java +++ b/dexlib/src/main/java/org/jf/dexlib/Util/DebugInfoBuilder.java @@ -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 diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/DeodexUtil.java b/dexlib/src/main/java/org/jf/dexlib/Util/DeodexUtil.java index 49683710..c0b66df7 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Util/DeodexUtil.java +++ b/dexlib/src/main/java/org/jf/dexlib/Util/DeodexUtil.java @@ -142,30 +142,28 @@ public class DeodexUtil { final ArrayList insns = new ArrayList(); final SparseArray insnsMap = new SparseArray(); - 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" + codeItem.getParent().method.getMethodName() + - codeItem.getParent().method.getPrototype().getPrototypeString()); + codeItem.getParent().method.getPrototype().getPrototypeString()); } List instructions = new ArrayList(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 insnsMap; /** - * Instructions that can execution could pass on to next + * Instructions that can execution could pass on to next */ public LinkedList successors = new LinkedList(); @@ -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 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 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 exceptionTypes = new ArrayList(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; } diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/Leb128Utils.java b/dexlib/src/main/java/org/jf/dexlib/Util/Leb128Utils.java index 5d5bd1a4..988bb21b 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Util/Leb128Utils.java +++ b/dexlib/src/main/java/org/jf/dexlib/Util/Leb128Utils.java @@ -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); + } } diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/SparseIntArray.java b/dexlib/src/main/java/org/jf/dexlib/Util/SparseIntArray.java new file mode 100644 index 00000000..232cad5e --- /dev/null +++ b/dexlib/src/main/java/org/jf/dexlib/Util/SparseIntArray.java @@ -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 0 + * 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 0...size()-1, returns + * the key from the indexth key-value mapping that this + * SparseIntArray stores. + */ + public int keyAt(int index) { + return mKeys[index]; + } + + /** + * Given an index in the range 0...size()-1, returns + * the value from the indexth 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; +} diff --git a/smali/src/main/antlr3/org/jf/smali/smaliTreeWalker.g b/smali/src/main/antlr3/org/jf/smali/smaliTreeWalker.g index 33f2f6fe..57d17ee2 100644 --- a/smali/src/main/antlr3/org/jf/smali/smaliTreeWalker.g +++ b/smali/src/main/antlr3/org/jf/smali/smaliTreeWalker.g @@ -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 referencedItems, int maxOutRegisters] +statements[int totalMethodRegisters, int methodParameterRegisters] returns[List instructions, int maxOutRegisters] @init { - ByteArrayOutput out = new ByteArrayOutput(); - $referencedItems = new LinkedList(); + $instructions = new LinkedList(); $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 referencedItems] returns[int outRegisters] +instruction[int totalMethodRegisters, int methodParameterRegisters, List 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 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));