/*
* Copyright 2012, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS 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.dexlib2.builder;
import com.google.common.collect.Lists;
import org.jf.dexlib2.Opcode;
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;
import java.util.List;
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<? extends Instruction> 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 testPayloadAlignmentAddNop() {
MethodImplementationBuilder implBuilder = new MethodImplementationBuilder(10);
implBuilder.addInstruction(new BuilderInstruction12x(Opcode.MOVE, 0, 0));
implBuilder.addInstruction(new BuilderArrayPayload(4, null));
List<? extends Instruction> instructions =
Lists.newArrayList(implBuilder.getMethodImplementation().getInstructions());
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<Instruction> 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<Instruction> 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<Instruction> instructions, Opcode[] expectedOpcodes) {
Assert.assertEquals(expectedOpcodes.length, instructions.size());
for (int i=0; i<expectedOpcodes.length; i++) {
Assert.assertEquals(instructions.get(i).getOpcode(), expectedOpcodes[i]);
}
}
@Test
public void testPackedSwitchAlignment() {
MethodImplementationBuilder implBuilder = new MethodImplementationBuilder(10);
implBuilder.addLabel("switch_target_1");
implBuilder.addInstruction(new BuilderInstruction10t(Opcode.GOTO, implBuilder.getLabel("goto_target")));
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<Instruction> 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() {
MethodImplementationBuilder implBuilder = new MethodImplementationBuilder(10);
implBuilder.addLabel("switch_target_1");
implBuilder.addInstruction(new BuilderInstruction10t(Opcode.GOTO, implBuilder.getLabel("goto_target")));
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")))));
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.SPARSE_SWITCH, 0,
implBuilder.getLabel("switch_payload")));
List<Instruction> 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());
}
}