1 /* 2 * Copyright 2012, Google Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * * Redistributions in binary form must reproduce the above 12 * copyright notice, this list of conditions and the following disclaimer 13 * in the documentation and/or other materials provided with the 14 * distribution. 15 * * Neither the name of Google Inc. nor the names of its 16 * contributors may be used to endorse or promote products derived from 17 * this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 package org.jf.dexlib2.builder; 33 34 import com.google.common.collect.Lists; 35 import org.jf.dexlib2.Opcode; 36 import org.jf.dexlib2.builder.instruction.*; 37 import org.jf.dexlib2.iface.instruction.Instruction; 38 import org.jf.dexlib2.iface.instruction.OffsetInstruction; 39 import org.jf.dexlib2.iface.instruction.formats.Instruction31t; 40 import org.jf.dexlib2.iface.instruction.formats.PackedSwitchPayload; 41 import org.jf.dexlib2.iface.instruction.formats.SparseSwitchPayload; 42 import org.junit.Assert; 43 import org.junit.Test; 44 45 import java.util.List; 46 47 public class PayloadAlignmentTest { 48 49 @Test 50 public void testPayloadAlignmentRemoveNop() { 51 MethodImplementationBuilder implBuilder = new MethodImplementationBuilder(10); 52 53 implBuilder.addInstruction(new BuilderInstruction10x(Opcode.NOP)); 54 implBuilder.addInstruction(new BuilderArrayPayload(4, null)); 55 56 List<? extends Instruction> instructions = 57 Lists.newArrayList(implBuilder.getMethodImplementation().getInstructions()); 58 59 Assert.assertEquals(instructions.size(), 1); 60 61 Instruction instruction = instructions.get(0); 62 63 Assert.assertEquals(instruction.getOpcode(), Opcode.ARRAY_PAYLOAD); 64 } 65 66 @Test 67 public void testPayloadAlignmentAddNop() { 68 MethodImplementationBuilder implBuilder = new MethodImplementationBuilder(10); 69 70 implBuilder.addInstruction(new BuilderInstruction12x(Opcode.MOVE, 0, 0)); 71 implBuilder.addInstruction(new BuilderArrayPayload(4, null)); 72 73 List<? extends Instruction> instructions = 74 Lists.newArrayList(implBuilder.getMethodImplementation().getInstructions()); 75 76 Assert.assertEquals(instructions.size(), 3); 77 78 Instruction instruction = instructions.get(0); 79 Assert.assertEquals(instruction.getOpcode(), Opcode.MOVE); 80 81 instruction = instructions.get(1); 82 Assert.assertEquals(instruction.getOpcode(), Opcode.NOP); 83 84 instruction = instructions.get(2); 85 Assert.assertEquals(instruction.getOpcode(), Opcode.ARRAY_PAYLOAD); 86 } 87 88 @Test 89 public void testPayloadAlignmentRemoveNopWithReferent() { 90 MethodImplementationBuilder implBuilder = new MethodImplementationBuilder(10); 91 92 Label label = implBuilder.getLabel("array_payload"); 93 implBuilder.addInstruction(new BuilderInstruction31t(Opcode.FILL_ARRAY_DATA, 0, label)); 94 implBuilder.addInstruction(new BuilderInstruction12x(Opcode.MOVE, 0, 0)); 95 implBuilder.addInstruction(new BuilderInstruction12x(Opcode.MOVE, 0, 0)); 96 implBuilder.addInstruction(new BuilderInstruction12x(Opcode.MOVE, 0, 0)); 97 implBuilder.addInstruction(new BuilderInstruction10x(Opcode.NOP)); 98 implBuilder.addLabel("array_payload"); 99 implBuilder.addInstruction(new BuilderArrayPayload(4, null)); 100 101 List<Instruction> instructions = Lists.newArrayList(implBuilder.getMethodImplementation().getInstructions()); 102 103 checkInstructions(instructions, 104 new Opcode[]{Opcode.FILL_ARRAY_DATA, 105 Opcode.MOVE, 106 Opcode.MOVE, 107 Opcode.MOVE, 108 Opcode.ARRAY_PAYLOAD}); 109 110 Instruction31t referent = (Instruction31t)instructions.get(0); 111 Assert.assertEquals(6, referent.getCodeOffset()); 112 } 113 114 @Test 115 public void testPayloadAlignmentAddNopWithReferent() { 116 MethodImplementationBuilder implBuilder = new MethodImplementationBuilder(10); 117 118 Label label = implBuilder.getLabel("array_payload"); 119 implBuilder.addInstruction(new BuilderInstruction31t(Opcode.FILL_ARRAY_DATA, 0, label)); 120 implBuilder.addInstruction(new BuilderInstruction12x(Opcode.MOVE, 0, 0)); 121 implBuilder.addInstruction(new BuilderInstruction12x(Opcode.MOVE, 0, 0)); 122 implBuilder.addInstruction(new BuilderInstruction12x(Opcode.MOVE, 0, 0)); 123 implBuilder.addInstruction(new BuilderInstruction12x(Opcode.MOVE, 0, 0)); 124 implBuilder.addLabel("array_payload"); 125 implBuilder.addInstruction(new BuilderArrayPayload(4, null)); 126 127 List<Instruction> instructions = Lists.newArrayList(implBuilder.getMethodImplementation().getInstructions()); 128 129 checkInstructions(instructions, 130 new Opcode[]{Opcode.FILL_ARRAY_DATA, 131 Opcode.MOVE, 132 Opcode.MOVE, 133 Opcode.MOVE, 134 Opcode.MOVE, 135 Opcode.NOP, 136 Opcode.ARRAY_PAYLOAD}); 137 138 Instruction31t referent = (Instruction31t)instructions.get(0); 139 Assert.assertEquals(8, referent.getCodeOffset()); 140 } 141 142 private static void checkInstructions(List<Instruction> instructions, Opcode[] expectedOpcodes) { 143 Assert.assertEquals(expectedOpcodes.length, instructions.size()); 144 145 for (int i=0; i<expectedOpcodes.length; i++) { 146 Assert.assertEquals(instructions.get(i).getOpcode(), expectedOpcodes[i]); 147 } 148 } 149 150 @Test 151 public void testPackedSwitchAlignment() { 152 MethodImplementationBuilder implBuilder = new MethodImplementationBuilder(10); 153 154 implBuilder.addLabel("switch_target_1"); 155 implBuilder.addInstruction(new BuilderInstruction10t(Opcode.GOTO, implBuilder.getLabel("goto_target"))); 156 157 implBuilder.addLabel("switch_payload"); 158 implBuilder.addInstruction(new BuilderPackedSwitchPayload(0, Lists.newArrayList( 159 implBuilder.getLabel("switch_target_1"), 160 implBuilder.getLabel("switch_target_2"), 161 implBuilder.getLabel("switch_target_3")))); 162 163 implBuilder.addLabel("goto_target"); 164 implBuilder.addInstruction(new BuilderInstruction10x(Opcode.NOP)); 165 implBuilder.addInstruction(new BuilderInstruction10x(Opcode.NOP)); 166 167 implBuilder.addLabel("switch_target_2"); 168 implBuilder.addInstruction(new BuilderInstruction10x(Opcode.NOP)); 169 170 implBuilder.addLabel("switch_target_3"); 171 implBuilder.addInstruction(new BuilderInstruction10x(Opcode.NOP)); 172 173 implBuilder.addInstruction(new BuilderInstruction31t(Opcode.PACKED_SWITCH, 0, 174 implBuilder.getLabel("switch_payload"))); 175 176 List<Instruction> instructions = Lists.newArrayList(implBuilder.getMethodImplementation().getInstructions()); 177 178 checkInstructions(instructions, 179 new Opcode[]{Opcode.GOTO, 180 Opcode.NOP, 181 Opcode.PACKED_SWITCH_PAYLOAD, 182 Opcode.NOP, 183 Opcode.NOP, 184 Opcode.NOP, 185 Opcode.NOP, 186 Opcode.PACKED_SWITCH}); 187 188 OffsetInstruction gotoInstruction = (OffsetInstruction)instructions.get(0); 189 Assert.assertEquals(12, gotoInstruction.getCodeOffset()); 190 191 PackedSwitchPayload payload = (PackedSwitchPayload)instructions.get(2); 192 Assert.assertEquals(3, payload.getSwitchElements().size()); 193 Assert.assertEquals(-16, payload.getSwitchElements().get(0).getOffset()); 194 Assert.assertEquals(-2, payload.getSwitchElements().get(1).getOffset()); 195 Assert.assertEquals(-1, payload.getSwitchElements().get(2).getOffset()); 196 197 OffsetInstruction referent = (OffsetInstruction)instructions.get(7); 198 Assert.assertEquals(-14, referent.getCodeOffset()); 199 } 200 201 @Test 202 public void testSparseSwitchAlignment() { 203 MethodImplementationBuilder implBuilder = new MethodImplementationBuilder(10); 204 205 implBuilder.addLabel("switch_target_1"); 206 implBuilder.addInstruction(new BuilderInstruction10t(Opcode.GOTO, implBuilder.getLabel("goto_target"))); 207 208 implBuilder.addLabel("switch_payload"); 209 implBuilder.addInstruction(new BuilderSparseSwitchPayload(Lists.newArrayList( 210 new SwitchLabelElement(0, implBuilder.getLabel("switch_target_1")), 211 new SwitchLabelElement(5, implBuilder.getLabel("switch_target_2")), 212 new SwitchLabelElement(10, implBuilder.getLabel("switch_target_3"))))); 213 214 implBuilder.addLabel("goto_target"); 215 implBuilder.addInstruction(new BuilderInstruction10x(Opcode.NOP)); 216 implBuilder.addInstruction(new BuilderInstruction10x(Opcode.NOP)); 217 218 implBuilder.addLabel("switch_target_2"); 219 implBuilder.addInstruction(new BuilderInstruction10x(Opcode.NOP)); 220 221 implBuilder.addLabel("switch_target_3"); 222 implBuilder.addInstruction(new BuilderInstruction10x(Opcode.NOP)); 223 224 implBuilder.addInstruction(new BuilderInstruction31t(Opcode.SPARSE_SWITCH, 0, 225 implBuilder.getLabel("switch_payload"))); 226 227 List<Instruction> instructions = Lists.newArrayList(implBuilder.getMethodImplementation().getInstructions()); 228 229 checkInstructions(instructions, 230 new Opcode[]{Opcode.GOTO, 231 Opcode.NOP, 232 Opcode.SPARSE_SWITCH_PAYLOAD, 233 Opcode.NOP, 234 Opcode.NOP, 235 Opcode.NOP, 236 Opcode.NOP, 237 Opcode.SPARSE_SWITCH}); 238 239 OffsetInstruction gotoInstruction = (OffsetInstruction)instructions.get(0); 240 Assert.assertEquals(16, gotoInstruction.getCodeOffset()); 241 242 SparseSwitchPayload payload = (SparseSwitchPayload)instructions.get(2); 243 Assert.assertEquals(3, payload.getSwitchElements().size()); 244 Assert.assertEquals(-20, payload.getSwitchElements().get(0).getOffset()); 245 Assert.assertEquals(-2, payload.getSwitchElements().get(1).getOffset()); 246 Assert.assertEquals(-1, payload.getSwitchElements().get(2).getOffset()); 247 248 OffsetInstruction referent = (OffsetInstruction)instructions.get(7); 249 Assert.assertEquals(-18, referent.getCodeOffset()); 250 } 251 } 252