JumboStringConversionTest.java revision 3d721348c55b6b7b68d48dafb9829adb5f1829d2
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 32package org.jf.dexlib2.writer; 33 34import com.google.common.collect.Lists; 35import junit.framework.Assert; 36import org.jf.dexlib2.Opcode; 37import org.jf.dexlib2.iface.instruction.Instruction; 38import org.jf.dexlib2.iface.instruction.SwitchElement; 39import org.jf.dexlib2.iface.instruction.formats.*; 40import org.jf.dexlib2.immutable.ImmutableMethodImplementation; 41import org.jf.dexlib2.immutable.instruction.*; 42import org.jf.dexlib2.immutable.reference.ImmutableStringReference; 43import org.jf.dexlib2.writer.util.InstructionWriteUtil; 44import org.junit.Before; 45import org.junit.Test; 46 47import java.util.ArrayList; 48 49public class JumboStringConversionTest { 50 private static final int MIN_NUM_JUMBO_STRINGS = 256; 51 52 private MockStringPool mStringPool; 53 ArrayList<String> mJumboStrings; 54 55 @Before 56 public void setup() { 57 mStringPool = new MockStringPool(); 58 StringBuilder stringBuilder = new StringBuilder("a"); 59 mJumboStrings = Lists.newArrayList(); 60 int index = 0; 61 62 // populate StringPool, make sure there are more than 64k+MIN_NUM_JUMBO_STRINGS strings 63 while (mJumboStrings.size()<MIN_NUM_JUMBO_STRINGS) { 64 for (int pos=stringBuilder.length()-1;pos>=0;pos--) { 65 for (char ch='a';ch<='z';ch++) { 66 stringBuilder.setCharAt(pos, ch); 67 mStringPool.intern(stringBuilder.toString(), index++); 68 if (mStringPool.getNumItems()>0xFFFF) { 69 mJumboStrings.add(stringBuilder.toString()); 70 } 71 } 72 } 73 74 stringBuilder.setLength(stringBuilder.length()+1); 75 for (int pos=0;pos<stringBuilder.length();pos++) { 76 stringBuilder.setCharAt(pos, 'a'); 77 } 78 } 79 } 80 81 @Test 82 public void testInstruction21c() { 83 ArrayList<ImmutableInstruction> instructions = Lists.newArrayList(); 84 85 ImmutableStringReference reference = new ImmutableStringReference(mJumboStrings.get(0)); 86 ImmutableInstruction21c instruction = new ImmutableInstruction21c(Opcode.CONST_STRING, 0, reference); 87 instructions.add(instruction); 88 89 ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null); 90 InstructionWriteUtil writeUtil = new InstructionWriteUtil(methodImplementation, mStringPool); 91 92 for (Instruction instr: writeUtil.getInstructions()) { 93 Assert.assertEquals("Jumbo string conversion was not performed!", instr.getOpcode(), Opcode.CONST_STRING_JUMBO); 94 } 95 } 96 97 private ArrayList<ImmutableInstruction> createSimpleInstructionList() { 98 ArrayList<ImmutableInstruction> instructions = Lists.newArrayList(); 99 100 ImmutableStringReference reference = new ImmutableStringReference(mJumboStrings.get(0)); 101 ImmutableInstruction21c stringInstr = new ImmutableInstruction21c(Opcode.CONST_STRING, 0, reference); 102 instructions.add(stringInstr); 103 104 reference = new ImmutableStringReference(mJumboStrings.get(1)); 105 stringInstr = new ImmutableInstruction21c(Opcode.CONST_STRING, 0, reference); 106 instructions.add(stringInstr); 107 108 ImmutableInstruction10x nopInstr = new ImmutableInstruction10x(Opcode.NOP); 109 instructions.add(nopInstr); 110 111 ArrayList<SwitchElement> switchElements = Lists.newArrayList(); 112 ImmutableSwitchElement switchElement = new ImmutableSwitchElement(0, 5); 113 switchElements.add(switchElement); 114 115 ImmutablePackedSwitchPayload packedSwitchInstr = new ImmutablePackedSwitchPayload(switchElements); 116 instructions.add(packedSwitchInstr); 117 118 ImmutableSparseSwitchPayload sparseSwitchPayload = new ImmutableSparseSwitchPayload(switchElements); 119 instructions.add(sparseSwitchPayload); 120 121 return instructions; 122 } 123 124 @Test 125 public void testInstruction10tSimple() { 126 ArrayList<ImmutableInstruction> instructions = createSimpleInstructionList(); 127 128 ImmutableInstruction10t gotoInstr = new ImmutableInstruction10t(Opcode.GOTO, 3); 129 instructions.add(1, gotoInstr); 130 131 ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null); 132 InstructionWriteUtil writeUtil = new InstructionWriteUtil(methodImplementation, mStringPool); 133 134 for (Instruction instr: writeUtil.getInstructions()) { 135 if (instr instanceof Instruction10t) { 136 Instruction10t instruction = (Instruction10t) instr; 137 Assert.assertEquals("goto (Format10t) target was not modified properly", instruction.getCodeOffset(), 4); 138 break; 139 } 140 } 141 } 142 143 @Test 144 public void testInstruction20tSimple() { 145 ArrayList<ImmutableInstruction> instructions = createSimpleInstructionList(); 146 147 ImmutableInstruction20t gotoInstr = new ImmutableInstruction20t(Opcode.GOTO_16, 4); 148 instructions.add(1, gotoInstr); 149 150 ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null); 151 InstructionWriteUtil writeUtil = new InstructionWriteUtil(methodImplementation, mStringPool); 152 153 for (Instruction instr: writeUtil.getInstructions()) { 154 if (instr instanceof Instruction20t) { 155 Instruction20t instruction = (Instruction20t) instr; 156 Assert.assertEquals("goto/16 (Format20t) target was not modified properly", instruction.getCodeOffset(), 5); 157 break; 158 } 159 } 160 } 161 162 @Test 163 public void testInstruction30t() { 164 ArrayList<ImmutableInstruction> instructions = createSimpleInstructionList(); 165 166 ImmutableInstruction30t gotoInstr = new ImmutableInstruction30t(Opcode.GOTO_32, 5); 167 instructions.add(1, gotoInstr); 168 169 ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null); 170 InstructionWriteUtil writeUtil = new InstructionWriteUtil(methodImplementation, mStringPool); 171 172 for (Instruction instr: writeUtil.getInstructions()) { 173 if (instr instanceof Instruction30t) { 174 Instruction30t instruction = (Instruction30t) instr; 175 Assert.assertEquals("goto/32 (Format30t) target was not modified properly", instruction.getCodeOffset(), 6); 176 break; 177 } 178 } 179 } 180 181 @Test 182 public void testInstruction21t() { 183 ArrayList<ImmutableInstruction> instructions = createSimpleInstructionList(); 184 185 ImmutableInstruction21t branchInstr = new ImmutableInstruction21t(Opcode.IF_EQZ, 0, 4); 186 instructions.add(1, branchInstr); 187 188 ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null); 189 InstructionWriteUtil writeUtil = new InstructionWriteUtil(methodImplementation, mStringPool); 190 191 for (Instruction instr: writeUtil.getInstructions()) { 192 if (instr instanceof Instruction21t) { 193 Instruction21t instruction = (Instruction21t) instr; 194 Assert.assertEquals("branch instruction (Format21t) target was not modified properly", instruction.getCodeOffset(), 5); 195 break; 196 } 197 } 198 } 199 200 @Test 201 public void testInstruction22t() { 202 ArrayList<ImmutableInstruction> instructions = createSimpleInstructionList(); 203 204 ImmutableInstruction22t branchInstr = new ImmutableInstruction22t(Opcode.IF_EQ, 0, 1, 4); 205 instructions.add(1, branchInstr); 206 207 ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null); 208 InstructionWriteUtil writeUtil = new InstructionWriteUtil(methodImplementation, mStringPool); 209 210 for (Instruction instr: writeUtil.getInstructions()) { 211 if (instr instanceof Instruction22t) { 212 Instruction22t instruction = (Instruction22t) instr; 213 Assert.assertEquals("branch instruction (Format22t) target was not modified properly", instruction.getCodeOffset(), 5); 214 break; 215 } 216 } 217 } 218 219 @Test 220 public void testInstruction31t() { 221 ArrayList<ImmutableInstruction> instructions = createSimpleInstructionList(); 222 223 ImmutableInstruction31t branchInstr = new ImmutableInstruction31t(Opcode.PACKED_SWITCH, 0, 5); 224 instructions.add(1, branchInstr); 225 226 ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null); 227 InstructionWriteUtil writeUtil = new InstructionWriteUtil(methodImplementation, mStringPool); 228 229 for (Instruction instr: writeUtil.getInstructions()) { 230 if (instr instanceof Instruction31t) { 231 Instruction31t instruction = (Instruction31t) instr; 232 Assert.assertEquals("branch instruction (Format31t) target was not modified properly", instruction.getCodeOffset(), 6); 233 break; 234 } 235 } 236 } 237 238 @Test 239 public void testPackedSwitchPayload() { 240 ArrayList<ImmutableInstruction> instructions = createSimpleInstructionList(); 241 242 ImmutableInstruction31t branchInstr = new ImmutableInstruction31t(Opcode.PACKED_SWITCH, 0, 6); 243 instructions.add(1, branchInstr); 244 245 ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null); 246 InstructionWriteUtil writeUtil = new InstructionWriteUtil(methodImplementation, mStringPool); 247 248 for (Instruction instr: writeUtil.getInstructions()) { 249 if (instr instanceof PackedSwitchPayload) { 250 PackedSwitchPayload instruction = (PackedSwitchPayload) instr; 251 for (SwitchElement switchElement: instruction.getSwitchElements()) { 252 Assert.assertEquals("packed switch payload offset was not modified properly", switchElement.getOffset(), 6); 253 } 254 break; 255 } 256 } 257 } 258 259 @Test 260 public void testSparseSwitchPayload() { 261 ArrayList<ImmutableInstruction> instructions = createSimpleInstructionList(); 262 263 ImmutableInstruction31t branchInstr = new ImmutableInstruction31t(Opcode.SPARSE_SWITCH, 0, 12); 264 instructions.add(1, branchInstr); 265 266 ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null); 267 InstructionWriteUtil writeUtil = new InstructionWriteUtil(methodImplementation, mStringPool); 268 269 for (Instruction instr: writeUtil.getInstructions()) { 270 if (instr instanceof SparseSwitchPayload) { 271 SparseSwitchPayload instruction = (SparseSwitchPayload) instr; 272 for (SwitchElement switchElement: instruction.getSwitchElements()) { 273 Assert.assertEquals("packed switch payload offset was not modified properly", switchElement.getOffset(), 6); 274 } 275 break; 276 } 277 } 278 } 279 280 @Test 281 public void testArrayPayloadAlignment() { 282 ArrayList<ImmutableInstruction> instructions = createSimpleInstructionList(); 283 284 // add misaligned array payload 285 ImmutableInstruction10x nopInstr = new ImmutableInstruction10x(Opcode.NOP); 286 instructions.add(nopInstr); 287 288 ImmutableArrayPayload arrayPayload = new ImmutableArrayPayload(4, null); 289 instructions.add(arrayPayload); 290 291 ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null); 292 InstructionWriteUtil writeUtil = new InstructionWriteUtil(methodImplementation, mStringPool); 293 294 int codeOffset = 0; 295 for (Instruction instr: writeUtil.getInstructions()) { 296 if (codeOffset == 21) { 297 Assert.assertEquals("array payload was not aligned properly", instr.getOpcode(), Opcode.NOP); 298 } 299 codeOffset += instr.getCodeUnits(); 300 } 301 } 302 303 @Test 304 public void testPackedSwitchAlignment() { 305 ArrayList<ImmutableInstruction> instructions = createSimpleInstructionList(); 306 307 // packed switch instruction is already misaligned 308 309 ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null); 310 InstructionWriteUtil writeUtil = new InstructionWriteUtil(methodImplementation, mStringPool); 311 312 int codeOffset = 0; 313 for (Instruction instr: writeUtil.getInstructions()) { 314 if (codeOffset == 7) { 315 Assert.assertEquals("packed switch payload was not aligned properly", instr.getOpcode(), Opcode.NOP); 316 } 317 codeOffset += instr.getCodeUnits(); 318 } 319 } 320 321 @Test 322 public void testSparseSwitchAlignment() { 323 ArrayList<ImmutableInstruction> instructions = createSimpleInstructionList(); 324 325 // insert a nop to mis-align sparse switch payload 326 ImmutableInstruction10x nopInstr = new ImmutableInstruction10x(Opcode.NOP); 327 instructions.add(4, nopInstr); 328 329 ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null); 330 InstructionWriteUtil writeUtil = new InstructionWriteUtil(methodImplementation, mStringPool); 331 332 int codeOffset = 0; 333 for (Instruction instr: writeUtil.getInstructions()) { 334 if (codeOffset == 15) { 335 Assert.assertEquals("packed switch payload was not aligned properly", instr.getOpcode(), Opcode.NOP); 336 } 337 codeOffset += instr.getCodeUnits(); 338 } 339 } 340 341 @Test 342 public void testGotoToGoto16() { 343 ArrayList<ImmutableInstruction> instructions = Lists.newArrayList(); 344 345 ImmutableInstruction10t gotoInstr = new ImmutableInstruction10t(Opcode.GOTO, 127); 346 instructions.add(gotoInstr); 347 348 ImmutableStringReference reference = new ImmutableStringReference(mJumboStrings.get(0)); 349 ImmutableInstruction21c stringInstr = new ImmutableInstruction21c(Opcode.CONST_STRING, 0, reference); 350 instructions.add(stringInstr); 351 352 for (int i=0;i<127;i++) { 353 ImmutableInstruction10x nopInstr = new ImmutableInstruction10x(Opcode.NOP); 354 instructions.add(nopInstr); 355 } 356 357 ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null); 358 InstructionWriteUtil writeUtil = new InstructionWriteUtil(methodImplementation, mStringPool); 359 360 Instruction instr = writeUtil.getInstructions().iterator().next(); 361 Assert.assertEquals("goto was not converted to goto/16 properly", instr.getOpcode(), Opcode.GOTO_16); 362 } 363 364 @Test 365 public void testGoto16ToGoto32() { 366 ArrayList<ImmutableInstruction> instructions = Lists.newArrayList(); 367 368 ImmutableInstruction20t gotoInstr = new ImmutableInstruction20t(Opcode.GOTO_16, Short.MAX_VALUE); 369 instructions.add(gotoInstr); 370 371 ImmutableStringReference reference = new ImmutableStringReference(mJumboStrings.get(0)); 372 ImmutableInstruction21c stringInstr = new ImmutableInstruction21c(Opcode.CONST_STRING, 0, reference); 373 instructions.add(stringInstr); 374 375 for (int i=0;i<Short.MAX_VALUE;i++) { 376 ImmutableInstruction10x nopInstr = new ImmutableInstruction10x(Opcode.NOP); 377 instructions.add(nopInstr); 378 } 379 380 ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null); 381 InstructionWriteUtil writeUtil = new InstructionWriteUtil(methodImplementation, mStringPool); 382 383 Instruction instr = writeUtil.getInstructions().iterator().next(); 384 Assert.assertEquals("goto/16 was not converted to goto/32 properly", instr.getOpcode(), Opcode.GOTO_32); 385 } 386 387 @Test 388 public void testGotoIterative() { 389 ArrayList<ImmutableInstruction> instructions = Lists.newArrayList(); 390 391 instructions.add(new ImmutableInstruction10t(Opcode.GOTO, 126)); 392 instructions.add(new ImmutableInstruction10t(Opcode.GOTO, 127)); 393 instructions.add(new ImmutableInstruction21c(Opcode.CONST_STRING, 0, new ImmutableStringReference(mJumboStrings.get(0)))); 394 for (int i=0;i<122;i++) { 395 instructions.add(new ImmutableInstruction10x(Opcode.NOP)); 396 } 397 instructions.add(new ImmutableInstruction21c(Opcode.CONST_STRING, 0, new ImmutableStringReference(mJumboStrings.get(1)))); 398 instructions.add(new ImmutableInstruction10x(Opcode.NOP)); 399 400 // this misaligned array payload will cause nop insertion on the first pass and its removal on the second pass 401 instructions.add(new ImmutableInstruction10x(Opcode.NOP)); 402 instructions.add(new ImmutableArrayPayload(4, null)); 403 404 ImmutableMethodImplementation methodImplementation = new ImmutableMethodImplementation(1, instructions, null, null); 405 InstructionWriteUtil writeUtil = new InstructionWriteUtil(methodImplementation, mStringPool); 406 407 Instruction instr = writeUtil.getInstructions().iterator().next(); 408 Assert.assertEquals("goto was not converted to goto/16 properly", instr.getOpcode(), Opcode.GOTO_16); 409 410 int codeOffset = 0; 411 for (Instruction instruction: writeUtil.getInstructions()) { 412 if (instruction instanceof ArrayPayload) { 413 Assert.assertEquals("packed switch payload was not aligned properly", codeOffset%2, 0); 414 } 415 codeOffset += instruction.getCodeUnits(); 416 } 417 } 418} 419