From 9a2de93a998958e582e9c2aa0bff7de602cc8771 Mon Sep 17 00:00:00 2001 From: Ben Gruver Date: Sun, 8 Sep 2013 21:41:23 -0700 Subject: [PATCH] Implement just-in-time instruction fixup due to string jumboification --- .../jf/dexlib2/builder/MethodLocation.java | 10 - .../builder/MutableMethodImplementation.java | 27 +- .../instruction/BuilderInstruction3rc.java | 3 +- .../instruction/BuilderSwitchElement.java | 2 +- .../dexbacked/raw/util/DexAnnotator.java | 3 +- .../org/jf/dexlib2/writer/ClassSection.java | 2 + .../java/org/jf/dexlib2/writer/DexWriter.java | 84 ++++-- .../org/jf/dexlib2/writer/StringSection.java | 1 + .../writer/builder/BuilderClassPool.java | 10 + .../writer/builder/BuilderStringPool.java | 4 + .../writer/io/MemoryDeferredOutputStream.java | 2 +- .../org/jf/dexlib2/writer/pool/ClassPool.java | 6 + .../jf/dexlib2/writer/pool/StringPool.java | 4 + .../dexlib2/writer/PayloadAlignmentTest.java | 255 ++++++++++++++---- smali/src/main/antlr3/smaliTreeWalker.g | 3 +- 15 files changed, 323 insertions(+), 93 deletions(-) diff --git a/dexlib2/src/main/java/org/jf/dexlib2/builder/MethodLocation.java b/dexlib2/src/main/java/org/jf/dexlib2/builder/MethodLocation.java index de94345c..862a4a1b 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/builder/MethodLocation.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/builder/MethodLocation.java @@ -54,16 +54,6 @@ public class MethodLocation { } debugItems.addAll(other.debugItems); other.debugItems = debugItems; - - for (int i=debugItems.size()-1; i>=0; i--) { - BuilderDebugItem debugItem = debugItems.get(i); - debugItem.location = other; - other.debugItems.add(0, debugItem); - } - for (BuilderDebugItem debugItem: debugItems) { - debugItem.location = other; - other.debugItems.add(0, debugItem); - } } @Nonnull diff --git a/dexlib2/src/main/java/org/jf/dexlib2/builder/MutableMethodImplementation.java b/dexlib2/src/main/java/org/jf/dexlib2/builder/MutableMethodImplementation.java index 0a6f8ac0..1f0579d0 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/builder/MutableMethodImplementation.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/builder/MutableMethodImplementation.java @@ -25,6 +25,10 @@ public class MutableMethodImplementation implements MethodImplementation { private final ArrayList tryBlocks = Lists.newArrayList(); private boolean fixInstructions = true; + public MutableMethodImplementation(@Nonnull MethodImplementation methodImplementation) { + throw new UnsupportedOperationException("not implemented yet."); + } + public MutableMethodImplementation(int registerCount) { this.registerCount = registerCount; } @@ -44,10 +48,16 @@ public class MutableMethodImplementation implements MethodImplementation { if (i >= size()) { throw new IndexOutOfBoundsException(); } + if (fixInstructions) { + fixInstructions(); + } return instructionList.get(i).instruction; } @Override public int size() { + if (fixInstructions) { + fixInstructions(); + } // don't include the last MethodLocation, which always has a null instruction return instructionList.size() - 1; } @@ -55,14 +65,24 @@ public class MutableMethodImplementation implements MethodImplementation { } @Nonnull @Override public List> getTryBlocks() { + if (fixInstructions) { + fixInstructions(); + } return Collections.unmodifiableList(tryBlocks); } @Nonnull @Override public Iterable getDebugItems() { + if (fixInstructions) { + fixInstructions(); + } return Iterables.concat( Iterables.transform(instructionList, new Function>() { @Nullable @Override public Iterable apply(@Nullable MethodLocation input) { assert input != null; + if (fixInstructions) { + throw new IllegalStateException("This iterator was invalidated by a change to" + + " this MutableMethodImplementation."); + } return input.getDebugItems(); } })); @@ -107,7 +127,7 @@ public class MutableMethodImplementation implements MethodImplementation { codeAddress += location.instruction.getCodeUnits(); } else { // only the last MethodLocation should have a null instruction - assert index == instructionList.size()-1; + assert i == instructionList.size()-1; } } @@ -117,6 +137,7 @@ public class MutableMethodImplementation implements MethodImplementation { public void addInstruction(@Nonnull BuilderInstruction instruction) { MethodLocation last = instructionList.get(instructionList.size()-1); last.instruction = instruction; + instruction.location = last; int nextCodeAddress = last.codeAddress + instruction.getCodeUnits(); instructionList.add(new MethodLocation(null, nextCodeAddress, instructionList.size())); @@ -149,6 +170,8 @@ public class MutableMethodImplementation implements MethodImplementation { assert i == instructionList.size() - 1; } } + + this.fixInstructions = true; } public void removeInstruction(int index) { @@ -164,7 +187,7 @@ public class MutableMethodImplementation implements MethodImplementation { instructionList.remove(index); int codeAddress = toRemove.codeAddress; for (int i=index; i getInstructions(@Nonnull MethodKey key); @Nonnull List> getTryBlocks(@Nonnull MethodKey key); @Nullable TypeKey getExceptionType(@Nonnull ExceptionHandler handler); + @Nonnull MutableMethodImplementation makeMutableMethodImplementation(@Nonnull MethodKey key); void setEncodedArrayOffset(@Nonnull ClassKey key, int offset); int getEncodedArrayOffset(@Nonnull ClassKey key); diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/DexWriter.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/DexWriter.java index 34d8f199..51671503 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/DexWriter.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/DexWriter.java @@ -31,10 +31,16 @@ package org.jf.dexlib2.writer; -import com.google.common.collect.*; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Ordering; import org.jf.dexlib2.AccessFlags; +import org.jf.dexlib2.Opcode; import org.jf.dexlib2.ReferenceType; import org.jf.dexlib2.base.BaseAnnotation; +import org.jf.dexlib2.builder.MutableMethodImplementation; +import org.jf.dexlib2.builder.instruction.BuilderInstruction31c; import org.jf.dexlib2.dexbacked.raw.*; import org.jf.dexlib2.iface.Annotation; import org.jf.dexlib2.iface.ExceptionHandler; @@ -42,6 +48,7 @@ import org.jf.dexlib2.iface.TryBlock; import org.jf.dexlib2.iface.debug.DebugItem; import org.jf.dexlib2.iface.debug.LineNumber; import org.jf.dexlib2.iface.instruction.Instruction; +import org.jf.dexlib2.iface.instruction.OneRegisterInstruction; import org.jf.dexlib2.iface.instruction.ReferenceInstruction; import org.jf.dexlib2.iface.instruction.formats.*; import org.jf.dexlib2.iface.reference.*; @@ -727,10 +734,39 @@ public abstract class DexWriter< Iterable methods = Iterables.concat(directMethods, virtualMethods); for (MethodKey methodKey: methods) { - int debugItemOffset = writeDebugItem(offsetWriter, debugWriter, methodKey); - int codeItemOffset = writeCodeItem(codeWriter, ehBuf, methodKey, debugItemOffset); + List> tryBlocks = + classSection.getTryBlocks(methodKey); + Iterable instructions = classSection.getInstructions(methodKey); + Iterable debugItems = classSection.getDebugItems(methodKey); - if (codeItemOffset != NO_OFFSET) { + if (instructions != null && stringSection.hasJumboIndexes()) { + boolean needsFix = false; + for (Instruction instruction: instructions) { + if (instruction.getOpcode() == Opcode.CONST_STRING) { + if (stringSection.getItemIndex( + (StringRef)((ReferenceInstruction)instruction).getReference()) >= 65536) { + needsFix = true; + break; + } + } + } + + if (needsFix) { + MutableMethodImplementation mutableMethodImplementation = + classSection.makeMutableMethodImplementation(methodKey); + fixInstructions(mutableMethodImplementation); + + instructions = mutableMethodImplementation.getInstructions(); + tryBlocks = mutableMethodImplementation.getTryBlocks(); + debugItems = mutableMethodImplementation.getDebugItems(); + } + } + + int debugItemOffset = writeDebugItem(offsetWriter, debugWriter, + classSection.getParameterNames(methodKey), debugItems); + int codeItemOffset = writeCodeItem(codeWriter, ehBuf, methodKey, tryBlocks, instructions, debugItemOffset); + + if (codeItemOffset != -1) { codeOffsets.add(new CodeItemOffset(methodKey, codeItemOffset)); } } @@ -739,6 +775,7 @@ public abstract class DexWriter< offsetWriter.align(); codeSectionOffset = offsetWriter.getPosition(); + codeWriter.close(); temp.writeTo(offsetWriter); temp.close(); @@ -747,12 +784,27 @@ public abstract class DexWriter< } } - private int writeDebugItem(@Nonnull DexDataWriter writer, - @Nonnull DebugWriter debugWriter, - @Nonnull MethodKey methodKey) throws IOException { - Iterable debugItems = classSection.getDebugItems(methodKey); - Iterable parameterNames = classSection.getParameterNames(methodKey); + private void fixInstructions(@Nonnull MutableMethodImplementation methodImplementation) { + List instructions = methodImplementation.getInstructions(); + for (int i=0; i 65536) { + methodImplementation.replaceInstruction(i, new BuilderInstruction31c(Opcode.CONST_STRING_JUMBO, + ((OneRegisterInstruction)instruction).getRegisterA(), + ((ReferenceInstruction)instruction).getReference())); + } + } + } + } + + private int writeDebugItem(@Nonnull DexDataWriter writer, + @Nonnull DebugWriter debugWriter, + @Nullable Iterable parameterNames, + @Nullable Iterable debugItems) throws IOException { int parameterCount = 0; if (parameterNames != null) { int index = 0; @@ -809,13 +861,13 @@ public abstract class DexWriter< } private int writeCodeItem(@Nonnull DexDataWriter writer, - @Nonnull ByteArrayOutputStream ehBuf, - @Nonnull MethodKey methodKey, - int debugItemOffset) throws IOException { - Iterable instructions = classSection.getInstructions(methodKey); - + @Nonnull ByteArrayOutputStream ehBuf, + @Nonnull MethodKey methodKey, + @Nonnull List> tryBlocks, + @Nullable Iterable instructions, + int debugItemOffset) throws IOException { if (instructions == null && debugItemOffset == NO_OFFSET) { - return NO_OFFSET; + return -1; } numCodeItemItems++; @@ -823,7 +875,6 @@ public abstract class DexWriter< writer.align(); int codeItemOffset = writer.getPosition(); - classSection.setCodeItemOffset(methodKey, writer.getPosition()); writer.writeUshort(classSection.getRegisterCount(methodKey)); @@ -831,7 +882,6 @@ public abstract class DexWriter< Collection parameters = typeListSection.getTypes( protoSection.getParameters(methodSection.getPrototype(methodKey))); - List> tryBlocks = classSection.getTryBlocks(methodKey); writer.writeUshort(MethodUtil.getParameterRegisterCount(parameters, isStatic)); if (instructions != null) { diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/StringSection.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/StringSection.java index 4e9c8d81..3f370ee8 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/StringSection.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/StringSection.java @@ -37,4 +37,5 @@ import javax.annotation.Nonnull; public interface StringSection extends NullableIndexSection { int getItemIndex(@Nonnull StringRef key); + boolean hasJumboIndexes(); } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderClassPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderClassPool.java index 8db77376..91f6d4c8 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderClassPool.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/BuilderClassPool.java @@ -35,6 +35,7 @@ import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.collect.*; import org.jf.dexlib2.DebugItemType; +import org.jf.dexlib2.builder.MutableMethodImplementation; import org.jf.dexlib2.iface.ExceptionHandler; import org.jf.dexlib2.iface.Field; import org.jf.dexlib2.iface.MethodImplementation; @@ -313,6 +314,15 @@ public class BuilderClassPool implements ClassSection 65536; + } + @Nonnull @Override public Collection> getItems() { return new BuilderMapEntryCollection(internedItems.values()) { @Override protected int getValue(@Nonnull BuilderStringReference key) { diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/io/MemoryDeferredOutputStream.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/io/MemoryDeferredOutputStream.java index 3de8a0e3..9e6e69b0 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/io/MemoryDeferredOutputStream.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/io/MemoryDeferredOutputStream.java @@ -53,7 +53,7 @@ public class MemoryDeferredOutputStream extends DeferredOutputStream { int remaining = remaining(); int written = 0; while (length - written > 0) { - int toWrite = Math.min(remaining, length); + int toWrite = Math.min(remaining, (length - written)); System.arraycopy(bytes, offset + written, currentBuffer, currentPosition, toWrite); written += toWrite; currentPosition += toWrite; diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/ClassPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/ClassPool.java index c3ab4487..a6f602ea 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/ClassPool.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/ClassPool.java @@ -36,6 +36,7 @@ import com.google.common.base.Predicate; import com.google.common.collect.*; import org.jf.dexlib2.DebugItemType; import org.jf.dexlib2.ReferenceType; +import org.jf.dexlib2.builder.MutableMethodImplementation; import org.jf.dexlib2.iface.*; import org.jf.dexlib2.iface.debug.*; import org.jf.dexlib2.iface.instruction.Instruction; @@ -433,6 +434,11 @@ public class ClassPool implements ClassSection 65536; + } } diff --git a/dexlib2/src/test/java/org/jf/dexlib2/writer/PayloadAlignmentTest.java b/dexlib2/src/test/java/org/jf/dexlib2/writer/PayloadAlignmentTest.java index d8a7916d..ffe35766 100644 --- a/dexlib2/src/test/java/org/jf/dexlib2/writer/PayloadAlignmentTest.java +++ b/dexlib2/src/test/java/org/jf/dexlib2/writer/PayloadAlignmentTest.java @@ -31,85 +31,224 @@ package org.jf.dexlib2.writer; -// TODO: uncomment/reimplement -/*public class PayloadAlignmentTest { - private MockStringIndexProvider mockStringIndexProvider; +import com.google.common.collect.Lists; +import org.jf.dexlib2.Opcode; +import org.jf.dexlib2.builder.Label; +import org.jf.dexlib2.builder.MethodImplementationBuilder; +import org.jf.dexlib2.builder.SwitchLabelElement; +import org.jf.dexlib2.builder.instruction.*; +import org.jf.dexlib2.iface.instruction.Instruction; +import org.jf.dexlib2.iface.instruction.OffsetInstruction; +import org.jf.dexlib2.iface.instruction.formats.Instruction31t; +import org.jf.dexlib2.iface.instruction.formats.PackedSwitchPayload; +import org.jf.dexlib2.iface.instruction.formats.SparseSwitchPayload; +import org.junit.Assert; +import org.junit.Test; - private class InsnWriteUtil extends InstructionWriteUtil { - public InsnWriteUtil(@Nonnull MethodImplementation implementation) { - super(implementation.getInstructions(), mockStringIndexProvider, ImmutableInstructionFactory.INSTANCE); - } - } +import java.util.List; - @Before - public void setup() { - mockStringIndexProvider = new MockStringIndexProvider(); +public class PayloadAlignmentTest { + + @Test + public void testPayloadAlignmentRemoveNop() { + MethodImplementationBuilder implBuilder = new MethodImplementationBuilder(10); + + implBuilder.addInstruction(new BuilderInstruction10x(Opcode.NOP)); + implBuilder.addInstruction(new BuilderArrayPayload(4, null)); + + List instructions = + Lists.newArrayList(implBuilder.getMethodImplementation().getInstructions()); + + Assert.assertEquals(instructions.size(), 1); + + Instruction instruction = instructions.get(0); + + Assert.assertEquals(instruction.getOpcode(), Opcode.ARRAY_PAYLOAD); } @Test - public void testArrayPayloadAlignment() { - ArrayList instructions = Lists.newArrayList(); + public void testPayloadAlignmentAddNop() { + MethodImplementationBuilder implBuilder = new MethodImplementationBuilder(10); - // add misaligned array payload - instructions.add(new ImmutableInstruction10x(Opcode.NOP)); - instructions.add(new ImmutableArrayPayload(4, null)); + implBuilder.addInstruction(new BuilderInstruction12x(Opcode.MOVE, 0, 0)); + implBuilder.addInstruction(new BuilderArrayPayload(4, null)); - ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null); - InsnWriteUtil writeUtil = new InsnWriteUtil(methodImplementation); + List instructions = + Lists.newArrayList(implBuilder.getMethodImplementation().getInstructions()); - int codeOffset = 0; - for (Instruction instr: writeUtil.getInstructions()) { - if (instr.getOpcode().equals(Opcode.ARRAY_PAYLOAD)) { - Assert.assertEquals("array payload was not aligned properly", codeOffset%2, 0); - break; - } - codeOffset += instr.getCodeUnits(); + Assert.assertEquals(instructions.size(), 3); + + Instruction instruction = instructions.get(0); + Assert.assertEquals(instruction.getOpcode(), Opcode.MOVE); + + instruction = instructions.get(1); + Assert.assertEquals(instruction.getOpcode(), Opcode.NOP); + + instruction = instructions.get(2); + Assert.assertEquals(instruction.getOpcode(), Opcode.ARRAY_PAYLOAD); + } + + @Test + public void testPayloadAlignmentRemoveNopWithReferent() { + MethodImplementationBuilder implBuilder = new MethodImplementationBuilder(10); + + Label label = implBuilder.getLabel("array_payload"); + implBuilder.addInstruction(new BuilderInstruction31t(Opcode.FILL_ARRAY_DATA, 0, label)); + implBuilder.addInstruction(new BuilderInstruction12x(Opcode.MOVE, 0, 0)); + implBuilder.addInstruction(new BuilderInstruction12x(Opcode.MOVE, 0, 0)); + implBuilder.addInstruction(new BuilderInstruction12x(Opcode.MOVE, 0, 0)); + implBuilder.addInstruction(new BuilderInstruction10x(Opcode.NOP)); + implBuilder.addLabel("array_payload"); + implBuilder.addInstruction(new BuilderArrayPayload(4, null)); + + List instructions = Lists.newArrayList(implBuilder.getMethodImplementation().getInstructions()); + + checkInstructions(instructions, + new Opcode[]{Opcode.FILL_ARRAY_DATA, + Opcode.MOVE, + Opcode.MOVE, + Opcode.MOVE, + Opcode.ARRAY_PAYLOAD}); + + Instruction31t referent = (Instruction31t)instructions.get(0); + Assert.assertEquals(6, referent.getCodeOffset()); + } + + @Test + public void testPayloadAlignmentAddNopWithReferent() { + MethodImplementationBuilder implBuilder = new MethodImplementationBuilder(10); + + Label label = implBuilder.getLabel("array_payload"); + implBuilder.addInstruction(new BuilderInstruction31t(Opcode.FILL_ARRAY_DATA, 0, label)); + implBuilder.addInstruction(new BuilderInstruction12x(Opcode.MOVE, 0, 0)); + implBuilder.addInstruction(new BuilderInstruction12x(Opcode.MOVE, 0, 0)); + implBuilder.addInstruction(new BuilderInstruction12x(Opcode.MOVE, 0, 0)); + implBuilder.addInstruction(new BuilderInstruction12x(Opcode.MOVE, 0, 0)); + implBuilder.addLabel("array_payload"); + implBuilder.addInstruction(new BuilderArrayPayload(4, null)); + + List instructions = Lists.newArrayList(implBuilder.getMethodImplementation().getInstructions()); + + checkInstructions(instructions, + new Opcode[]{Opcode.FILL_ARRAY_DATA, + Opcode.MOVE, + Opcode.MOVE, + Opcode.MOVE, + Opcode.MOVE, + Opcode.NOP, + Opcode.ARRAY_PAYLOAD}); + + Instruction31t referent = (Instruction31t)instructions.get(0); + Assert.assertEquals(8, referent.getCodeOffset()); + } + + private static void checkInstructions(List instructions, Opcode[] expectedOpcodes) { + Assert.assertEquals(expectedOpcodes.length, instructions.size()); + + for (int i=0; i instructions = Lists.newArrayList(); - // add misaligned packed switch payload - ArrayList switchElements = Lists.newArrayList(); - switchElements.add(new ImmutableSwitchElement(0, 5)); - instructions.add(new ImmutableInstruction10x(Opcode.NOP)); - instructions.add(new ImmutablePackedSwitchPayload(switchElements)); + MethodImplementationBuilder implBuilder = new MethodImplementationBuilder(10); - ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null); - InsnWriteUtil writeUtil = new InsnWriteUtil(methodImplementation); + implBuilder.addLabel("switch_target_1"); + implBuilder.addInstruction(new BuilderInstruction10t(Opcode.GOTO, implBuilder.getLabel("goto_target"))); - int codeOffset = 0; - for (Instruction instr: writeUtil.getInstructions()) { - if (instr.getOpcode().equals(Opcode.PACKED_SWITCH_PAYLOAD)) { - Assert.assertEquals("packed switch payload was not aligned properly", codeOffset%2, 0); - break; - } - codeOffset += instr.getCodeUnits(); - } + implBuilder.addLabel("switch_payload"); + implBuilder.addInstruction(new BuilderPackedSwitchPayload(0, Lists.newArrayList( + implBuilder.getLabel("switch_target_1"), + implBuilder.getLabel("switch_target_2"), + implBuilder.getLabel("switch_target_3")))); + + implBuilder.addLabel("goto_target"); + implBuilder.addInstruction(new BuilderInstruction10x(Opcode.NOP)); + implBuilder.addInstruction(new BuilderInstruction10x(Opcode.NOP)); + + implBuilder.addLabel("switch_target_2"); + implBuilder.addInstruction(new BuilderInstruction10x(Opcode.NOP)); + + implBuilder.addLabel("switch_target_3"); + implBuilder.addInstruction(new BuilderInstruction10x(Opcode.NOP)); + + implBuilder.addInstruction(new BuilderInstruction31t(Opcode.PACKED_SWITCH, 0, + implBuilder.getLabel("switch_payload"))); + + List instructions = Lists.newArrayList(implBuilder.getMethodImplementation().getInstructions()); + + checkInstructions(instructions, + new Opcode[]{Opcode.GOTO, + Opcode.NOP, + Opcode.PACKED_SWITCH_PAYLOAD, + Opcode.NOP, + Opcode.NOP, + Opcode.NOP, + Opcode.NOP, + Opcode.PACKED_SWITCH}); + + OffsetInstruction gotoInstruction = (OffsetInstruction)instructions.get(0); + Assert.assertEquals(12, gotoInstruction.getCodeOffset()); + + PackedSwitchPayload payload = (PackedSwitchPayload)instructions.get(2); + Assert.assertEquals(3, payload.getSwitchElements().size()); + Assert.assertEquals(-16, payload.getSwitchElements().get(0).getOffset()); + Assert.assertEquals(-2, payload.getSwitchElements().get(1).getOffset()); + Assert.assertEquals(-1, payload.getSwitchElements().get(2).getOffset()); + + OffsetInstruction referent = (OffsetInstruction)instructions.get(7); + Assert.assertEquals(-14, referent.getCodeOffset()); } @Test public void testSparseSwitchAlignment() { - ArrayList instructions = Lists.newArrayList(); + MethodImplementationBuilder implBuilder = new MethodImplementationBuilder(10); - // add misaligned sparse switch payload - ArrayList switchElements = Lists.newArrayList(); - switchElements.add(new ImmutableSwitchElement(0, 5)); + implBuilder.addLabel("switch_target_1"); + implBuilder.addInstruction(new BuilderInstruction10t(Opcode.GOTO, implBuilder.getLabel("goto_target"))); - instructions.add(new ImmutableInstruction10x(Opcode.NOP)); - instructions.add(new ImmutableSparseSwitchPayload(switchElements)); + implBuilder.addLabel("switch_payload"); + implBuilder.addInstruction(new BuilderSparseSwitchPayload(Lists.newArrayList( + new SwitchLabelElement(0, implBuilder.getLabel("switch_target_1")), + new SwitchLabelElement(5, implBuilder.getLabel("switch_target_2")), + new SwitchLabelElement(10, implBuilder.getLabel("switch_target_3"))))); - ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null); - InsnWriteUtil writeUtil = new InsnWriteUtil(methodImplementation); + implBuilder.addLabel("goto_target"); + implBuilder.addInstruction(new BuilderInstruction10x(Opcode.NOP)); + implBuilder.addInstruction(new BuilderInstruction10x(Opcode.NOP)); - int codeOffset = 0; - for (Instruction instr: writeUtil.getInstructions()) { - if (instr.getOpcode().equals(Opcode.SPARSE_SWITCH_PAYLOAD)) { - Assert.assertEquals("packed switch payload was not aligned properly", codeOffset%2, 0); - break; - } - codeOffset += instr.getCodeUnits(); - } + implBuilder.addLabel("switch_target_2"); + implBuilder.addInstruction(new BuilderInstruction10x(Opcode.NOP)); + + implBuilder.addLabel("switch_target_3"); + implBuilder.addInstruction(new BuilderInstruction10x(Opcode.NOP)); + + implBuilder.addInstruction(new BuilderInstruction31t(Opcode.SPARSE_SWITCH, 0, + implBuilder.getLabel("switch_payload"))); + + List instructions = Lists.newArrayList(implBuilder.getMethodImplementation().getInstructions()); + + checkInstructions(instructions, + new Opcode[]{Opcode.GOTO, + Opcode.NOP, + Opcode.SPARSE_SWITCH_PAYLOAD, + Opcode.NOP, + Opcode.NOP, + Opcode.NOP, + Opcode.NOP, + Opcode.SPARSE_SWITCH}); + + OffsetInstruction gotoInstruction = (OffsetInstruction)instructions.get(0); + Assert.assertEquals(16, gotoInstruction.getCodeOffset()); + + SparseSwitchPayload payload = (SparseSwitchPayload)instructions.get(2); + Assert.assertEquals(3, payload.getSwitchElements().size()); + Assert.assertEquals(-20, payload.getSwitchElements().get(0).getOffset()); + Assert.assertEquals(-2, payload.getSwitchElements().get(1).getOffset()); + Assert.assertEquals(-1, payload.getSwitchElements().get(2).getOffset()); + + OffsetInstruction referent = (OffsetInstruction)instructions.get(7); + Assert.assertEquals(-18, referent.getCodeOffset()); } -}*/ +} diff --git a/smali/src/main/antlr3/smaliTreeWalker.g b/smali/src/main/antlr3/smaliTreeWalker.g index 5714a2d8..035c8724 100644 --- a/smali/src/main/antlr3/smaliTreeWalker.g +++ b/smali/src/main/antlr3/smaliTreeWalker.g @@ -538,7 +538,8 @@ catches returns[List tryBlocks] catch_directive : ^(I_CATCH nonvoid_type_descriptor from=label_ref to=label_ref using=label_ref) { - $method::methodBuilder.addCatch($nonvoid_type_descriptor.type, $from.label, $to.label, $using.label); + $method::methodBuilder.addCatch(dexBuilder.internTypeReference($nonvoid_type_descriptor.type), + $from.label, $to.label, $using.label); }; catchall_directive