1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package dexfuzz.program; 18 19 import dexfuzz.Log; 20 import dexfuzz.rawdex.CodeItem; 21 import dexfuzz.rawdex.EncodedCatchHandler; 22 import dexfuzz.rawdex.EncodedTypeAddrPair; 23 import dexfuzz.rawdex.Instruction; 24 import dexfuzz.rawdex.Opcode; 25 import dexfuzz.rawdex.TryItem; 26 import dexfuzz.rawdex.formats.ContainsTarget; 27 import dexfuzz.rawdex.formats.RawInsnHelper; 28 29 import java.util.ArrayList; 30 import java.util.Collections; 31 import java.util.Comparator; 32 import java.util.HashMap; 33 import java.util.LinkedList; 34 import java.util.List; 35 import java.util.Map; 36 37 /** 38 * Translates from a CodeItem (the raw list of Instructions) to MutatableCode 39 * (graph of Instructions, using MInsns and subclasses) and vice-versa. 40 */ 41 public class CodeTranslator { 42 43 /** 44 * Given a raw DEX file's CodeItem, produce a MutatableCode object, that CodeMutators 45 * are designed to operate on. 46 * @param codeItemIdx Used to make sure the correct CodeItem is updated later after mutation. 47 * @return A new MutatableCode object, which contains all relevant information 48 * obtained from the CodeItem. 49 */ codeItemToMutatableCode(Program program, CodeItem codeItem, int codeItemIdx, int mutatableCodeIdx)50 public MutatableCode codeItemToMutatableCode(Program program, CodeItem codeItem, 51 int codeItemIdx, int mutatableCodeIdx) { 52 Log.debug("Translating CodeItem " + codeItemIdx 53 + " (" + codeItem.meta.methodName + ") to MutatableCode"); 54 55 MutatableCode mutatableCode = new MutatableCode(program); 56 57 codeItem.registerMutatableCode(mutatableCode); 58 59 mutatableCode.name = codeItem.meta.methodName; 60 mutatableCode.shorty = codeItem.meta.shorty; 61 mutatableCode.isStatic = codeItem.meta.isStatic; 62 63 mutatableCode.codeItemIdx = codeItemIdx; 64 65 mutatableCode.mutatableCodeIdx = mutatableCodeIdx; 66 67 mutatableCode.registersSize = codeItem.registersSize; 68 mutatableCode.insSize = codeItem.insSize; 69 mutatableCode.outsSize = codeItem.outsSize; 70 mutatableCode.triesSize = codeItem.triesSize; 71 72 // Temporary map from bytecode offset -> instruction. 73 Map<Integer,MInsn> insnLocationMap = new HashMap<Integer,MInsn>(); 74 75 List<Instruction> inputInsns = codeItem.insns; 76 77 // Create the MInsns. 78 int loc = 0; 79 for (Instruction insn : inputInsns) { 80 MInsn mInsn = null; 81 82 if (isInstructionSwitch(insn)) { 83 mInsn = new MSwitchInsn(); 84 } else if (isInstructionBranch(insn)) { 85 mInsn = new MBranchInsn(); 86 } else if (isInstructionFillArrayData(insn)) { 87 mInsn = new MInsnWithData(); 88 } else { 89 mInsn = new MInsn(); 90 } 91 92 mInsn.insn = insn; 93 94 // Populate the temporary map. 95 insnLocationMap.put(loc, mInsn); 96 97 // Populate the proper list of mutatable instructions. 98 mutatableCode.addInstructionToEnd(mInsn); 99 100 // Calculate the offsets for each instruction. 101 mInsn.location = loc; 102 mInsn.locationUpdated = false; 103 104 loc += mInsn.insn.getSize(); 105 } 106 107 // Now make branch/switch instructions point at the right target instructions. 108 for (MInsn mInsn : mutatableCode.getInstructions()) { 109 if (mInsn instanceof MSwitchInsn) { 110 readSwitchInstruction((MSwitchInsn) mInsn, insnLocationMap); 111 } else if (mInsn instanceof MInsnWithData) { 112 ContainsTarget containsTarget = (ContainsTarget) mInsn.insn.info.format; 113 int targetLoc = mInsn.location + (int) containsTarget.getTarget(mInsn.insn); 114 ((MInsnWithData)mInsn).dataTarget = insnLocationMap.get(targetLoc); 115 if (((MInsnWithData)mInsn).dataTarget == null) { 116 Log.errorAndQuit("Bad offset calculation in data-target insn"); 117 } 118 } else if (mInsn instanceof MBranchInsn) { 119 ContainsTarget containsTarget = (ContainsTarget) mInsn.insn.info.format; 120 int targetLoc = mInsn.location + (int) containsTarget.getTarget(mInsn.insn); 121 ((MBranchInsn)mInsn).target = insnLocationMap.get(targetLoc); 122 if (((MBranchInsn)mInsn).target == null) { 123 Log.errorAndQuit("Bad offset calculation in branch insn"); 124 } 125 } 126 } 127 128 // Now create try blocks. 129 if (mutatableCode.triesSize > 0) { 130 readTryBlocks(codeItem, mutatableCode, insnLocationMap); 131 } 132 133 return mutatableCode; 134 } 135 136 /** 137 * Given a MutatableCode item that may have been mutated, update the original CodeItem 138 * correctly, to allow valid DEX to be written back to the output file. 139 */ mutatableCodeToCodeItem(CodeItem codeItem, MutatableCode mutatableCode)140 public void mutatableCodeToCodeItem(CodeItem codeItem, MutatableCode mutatableCode) { 141 Log.debug("Translating MutatableCode " + mutatableCode.name 142 + " to CodeItem " + mutatableCode.codeItemIdx); 143 144 // We must first align any data instructions at the end of the code 145 // before we recalculate any offsets. 146 // This also updates their sizes... 147 alignDataInstructions(mutatableCode); 148 149 // Validate that the tracked locations for instructions are valid. 150 // Also mark locations as no longer being updated. 151 int loc = 0; 152 for (MInsn mInsn : mutatableCode.getInstructions()) { 153 if (mInsn.insn.justRaw) { 154 // All just_raw instructions need alignment! 155 if ((loc % 2) != 0) { 156 loc++; 157 } 158 } 159 if (mInsn.location != loc) { 160 Log.errorAndQuit(String.format("%s does not have expected location 0x%x", 161 mInsn, loc)); 162 } 163 mInsn.locationUpdated = false; 164 loc += mInsn.insn.getSize(); 165 } 166 167 // This new list will be attached to the CodeItem at the end... 168 List<Instruction> outputInsns = new LinkedList<Instruction>(); 169 170 // Go through our new list of MInsns, adding them to the new 171 // list of instructions that will be attached to the CodeItem. 172 // Also recalculate offsets for branches. 173 for (MInsn mInsn : mutatableCode.getInstructions()) { 174 if (mInsn instanceof MSwitchInsn) { 175 updateSwitchInstruction((MSwitchInsn)mInsn, mutatableCode); 176 } else if (mInsn instanceof MInsnWithData) { 177 MInsn target = ((MInsnWithData) mInsn).dataTarget; 178 int dataOffset = target.location - mInsn.location; 179 ContainsTarget containsTarget = (ContainsTarget) mInsn.insn.info.format; 180 containsTarget.setTarget(mInsn.insn, dataOffset); 181 } else if (mInsn instanceof MBranchInsn) { 182 MInsn target = ((MBranchInsn) mInsn).target; 183 int branchOffset = target.location - mInsn.location; 184 ContainsTarget containsTarget = (ContainsTarget) mInsn.insn.info.format; 185 containsTarget.setTarget(mInsn.insn, branchOffset); 186 } 187 outputInsns.add(mInsn.insn); 188 } 189 190 // Calculate the new insns_size. 191 int newInsnsSize = 0; 192 for (Instruction insn : outputInsns) { 193 newInsnsSize += insn.getSize(); 194 } 195 196 if (mutatableCode.triesSize > 0) { 197 updateTryBlocks(codeItem, mutatableCode); 198 } 199 200 codeItem.insnsSize = newInsnsSize; 201 codeItem.insns = outputInsns; 202 codeItem.registersSize = mutatableCode.registersSize; 203 codeItem.insSize = mutatableCode.insSize; 204 codeItem.outsSize = mutatableCode.outsSize; 205 codeItem.triesSize = mutatableCode.triesSize; 206 } 207 208 /** 209 * The TryItem specifies an offset into the EncodedCatchHandlerList for a given CodeItem, 210 * but we only have an array of the EncodedCatchHandlers that the List contains. 211 * This function produces a map that offers a way to find out the index into our array, 212 * from the try handler's offset. 213 */ createTryHandlerOffsetToIndexMap(CodeItem codeItem)214 private Map<Short,Integer> createTryHandlerOffsetToIndexMap(CodeItem codeItem) { 215 // Create a sorted set of offsets. 216 List<Short> uniqueOffsets = new ArrayList<Short>(); 217 for (TryItem tryItem : codeItem.tries) { 218 int index = 0; 219 while (true) { 220 if ((index == uniqueOffsets.size()) 221 || (uniqueOffsets.get(index) > tryItem.handlerOff)) { 222 // First condition means we're at the end of the set (or we're inserting 223 // into an empty set) 224 // Second condition means that the offset belongs here 225 // ...so insert it here, pushing the element currently in this position to the 226 // right, if it exists 227 uniqueOffsets.add(index, tryItem.handlerOff); 228 break; 229 } else if (uniqueOffsets.get(index) == tryItem.handlerOff) { 230 // We've already seen it, and we're making a set, not a list. 231 break; 232 } else { 233 // Keep searching. 234 index++; 235 } 236 } 237 } 238 // Now we have an (implicit) index -> offset mapping! 239 240 // Now create the reverse mapping. 241 Map<Short,Integer> offsetIndexMap = new HashMap<Short,Integer>(); 242 for (int i = 0; i < uniqueOffsets.size(); i++) { 243 offsetIndexMap.put(uniqueOffsets.get(i), i); 244 } 245 246 return offsetIndexMap; 247 } 248 readTryBlocks(CodeItem codeItem, MutatableCode mutatableCode, Map<Integer,MInsn> insnLocationMap)249 private void readTryBlocks(CodeItem codeItem, MutatableCode mutatableCode, 250 Map<Integer,MInsn> insnLocationMap) { 251 mutatableCode.mutatableTries = new LinkedList<MTryBlock>(); 252 253 Map<Short,Integer> offsetIndexMap = createTryHandlerOffsetToIndexMap(codeItem); 254 255 // Read each TryItem into a MutatableTryBlock. 256 for (TryItem tryItem : codeItem.tries) { 257 MTryBlock mTryBlock = new MTryBlock(); 258 259 // Get the MInsns that form the start and end of the try block. 260 int startLocation = tryItem.startAddr; 261 mTryBlock.startInsn = insnLocationMap.get(startLocation); 262 263 // The instructions vary in size, so we have to find the last instruction in the block in a 264 // few tries. 265 int endLocation = tryItem.startAddr + tryItem.insnCount - 1; 266 mTryBlock.endInsn = insnLocationMap.get(endLocation); 267 while ((mTryBlock.endInsn == null) && (endLocation >= startLocation)) { 268 endLocation--; 269 mTryBlock.endInsn = insnLocationMap.get(endLocation); 270 } 271 272 // Sanity checks. 273 if (mTryBlock.startInsn == null) { 274 Log.errorAndQuit(String.format( 275 "Couldn't find a mutatable insn at start offset 0x%x", 276 startLocation)); 277 } 278 if (mTryBlock.endInsn == null) { 279 Log.errorAndQuit(String.format( 280 "Couldn't find a mutatable insn at end offset 0x%x", 281 endLocation)); 282 } 283 284 // Get the EncodedCatchHandler. 285 int handlerIdx = offsetIndexMap.get(tryItem.handlerOff); 286 EncodedCatchHandler encodedCatchHandler = codeItem.handlers.list[handlerIdx]; 287 288 // Do we have a catch all? If so, associate the MInsn that's there. 289 if (encodedCatchHandler.size <= 0) { 290 mTryBlock.catchAllHandler = 291 insnLocationMap.get(encodedCatchHandler.catchAllAddr); 292 // Sanity check. 293 if (mTryBlock.catchAllHandler == null) { 294 Log.errorAndQuit( 295 String.format("Couldn't find a mutatable insn at catch-all offset 0x%x", 296 encodedCatchHandler.catchAllAddr)); 297 } 298 } 299 // Do we have explicitly-typed handlers? This will remain empty if not. 300 mTryBlock.handlers = new LinkedList<MInsn>(); 301 302 // Associate all the explicitly-typed handlers. 303 for (int i = 0; i < Math.abs(encodedCatchHandler.size); i++) { 304 EncodedTypeAddrPair handler = encodedCatchHandler.handlers[i]; 305 MInsn handlerInsn = insnLocationMap.get(handler.addr); 306 // Sanity check. 307 if (handlerInsn == null) { 308 Log.errorAndQuit(String.format( 309 "Couldn't find a mutatable instruction at handler offset 0x%x", 310 handler.addr)); 311 } 312 mTryBlock.handlers.add(handlerInsn); 313 } 314 315 // Now finally add the new MutatableTryBlock into this MutatableCode's list! 316 mutatableCode.mutatableTries.add(mTryBlock); 317 } 318 } 319 updateTryBlocks(CodeItem codeItem, MutatableCode mutatableCode)320 private void updateTryBlocks(CodeItem codeItem, MutatableCode mutatableCode) { 321 322 // TODO: Support ability to add extra try blocks/handlers? 323 324 for (MTryBlock mTryBlock : mutatableCode.mutatableTries) { 325 if (mTryBlock.startInsn.location > mTryBlock.endInsn.location) { 326 // Mutation has put this try block's end insn before its start insn. Fix this. 327 MInsn tempInsn = mTryBlock.startInsn; 328 mTryBlock.startInsn = mTryBlock.endInsn; 329 mTryBlock.endInsn = tempInsn; 330 } 331 } 332 333 // First, manipulate the try blocks if they overlap. 334 for (int i = 0; i < mutatableCode.mutatableTries.size() - 1; i++) { 335 MTryBlock first = mutatableCode.mutatableTries.get(i); 336 MTryBlock second = mutatableCode.mutatableTries.get(i + 1); 337 338 // Do they overlap? 339 if (first.endInsn.location > second.startInsn.location) { 340 341 Log.debug("Found overlap in TryBlocks, moving 2nd TryBlock..."); 342 Log.debug("1st TryBlock goes from " + first.startInsn + " to " + first.endInsn); 343 Log.debug("2nd TryBlock goes from " + second.startInsn + " to " + second.endInsn); 344 345 // Find the first instruction that comes after that does not overlap 346 // with the first try block. 347 MInsn newInsn = second.startInsn; 348 int ptr = mutatableCode.getInstructionIndex(newInsn); 349 while (first.endInsn.location > newInsn.location) { 350 ptr++; 351 newInsn = mutatableCode.getInstructionAt(ptr); 352 } 353 second.startInsn = newInsn; 354 355 Log.debug("Now 2nd TryBlock goes from " + second.startInsn + " to " + second.endInsn); 356 } 357 } 358 359 Map<Short,Integer> offsetIndexMap = createTryHandlerOffsetToIndexMap(codeItem); 360 361 int tryItemIdx = 0; 362 for (MTryBlock mTryBlock : mutatableCode.mutatableTries) { 363 TryItem tryItem = codeItem.tries[tryItemIdx]; 364 365 tryItem.startAddr = mTryBlock.startInsn.location; 366 int insnCount = mTryBlock.endInsn.location - mTryBlock.startInsn.location + 367 mTryBlock.endInsn.insn.getSize(); 368 tryItem.insnCount = (short) insnCount; 369 370 // Get the EncodedCatchHandler. 371 EncodedCatchHandler encodedCatchHandler = 372 codeItem.handlers.list[offsetIndexMap.get(tryItem.handlerOff)]; 373 374 if (encodedCatchHandler.size <= 0) { 375 encodedCatchHandler.catchAllAddr = mTryBlock.catchAllHandler.location; 376 } 377 for (int i = 0; i < Math.abs(encodedCatchHandler.size); i++) { 378 MInsn handlerInsn = mTryBlock.handlers.get(i); 379 EncodedTypeAddrPair handler = encodedCatchHandler.handlers[i]; 380 handler.addr = handlerInsn.location; 381 } 382 tryItemIdx++; 383 } 384 } 385 386 /** 387 * Given a switch instruction, find the associated data's raw[] form, and update 388 * the targets of the switch instruction to point to the correct instructions. 389 */ readSwitchInstruction(MSwitchInsn switchInsn, Map<Integer,MInsn> insnLocationMap)390 private void readSwitchInstruction(MSwitchInsn switchInsn, 391 Map<Integer,MInsn> insnLocationMap) { 392 // Find the data. 393 ContainsTarget containsTarget = (ContainsTarget) switchInsn.insn.info.format; 394 int dataLocation = switchInsn.location + (int) containsTarget.getTarget(switchInsn.insn); 395 switchInsn.dataTarget = insnLocationMap.get(dataLocation); 396 if (switchInsn.dataTarget == null) { 397 Log.errorAndQuit("Bad offset calculation for data target in switch insn"); 398 } 399 400 // Now read the data. 401 Instruction dataInsn = switchInsn.dataTarget.insn; 402 403 int rawPtr = 2; 404 405 int targetsSize = (int) RawInsnHelper.getUnsignedShortFromTwoBytes(dataInsn.rawBytes, rawPtr); 406 rawPtr += 2; 407 408 int[] keys = new int[targetsSize]; 409 int[] targets = new int[targetsSize]; 410 411 if (dataInsn.rawType == 1) { 412 switchInsn.packed = true; 413 // Dealing with a packed-switch. 414 // Read the first key. 415 keys[0] = (int) RawInsnHelper.getUnsignedIntFromFourBytes(dataInsn.rawBytes, rawPtr); 416 rawPtr += 4; 417 // Calculate the rest of the keys. 418 for (int i = 1; i < targetsSize; i++) { 419 keys[i] = keys[i - 1] + 1; 420 } 421 } else if (dataInsn.rawType == 2) { 422 // Dealing with a sparse-switch. 423 // Read all of the keys. 424 for (int i = 0; i < targetsSize; i++) { 425 keys[i] = (int) RawInsnHelper.getUnsignedIntFromFourBytes(dataInsn.rawBytes, 426 rawPtr); 427 rawPtr += 4; 428 } 429 } 430 431 // Now read the targets. 432 for (int i = 0; i < targetsSize; i++) { 433 targets[i] = (int) RawInsnHelper.getUnsignedIntFromFourBytes(dataInsn.rawBytes, 434 rawPtr); 435 rawPtr += 4; 436 } 437 438 // Store the keys. 439 switchInsn.keys = keys; 440 441 // Convert our targets[] offsets into pointers to MInsns. 442 for (int target : targets) { 443 int targetLocation = switchInsn.location + target; 444 MInsn targetInsn = insnLocationMap.get(targetLocation); 445 switchInsn.targets.add(targetInsn); 446 if (targetInsn == null) { 447 Log.errorAndQuit("Bad offset calculation for target in switch insn"); 448 } 449 } 450 } 451 452 /** 453 * Given a mutatable switch instruction, which may have had some of its branch 454 * targets moved, update all the target offsets in the raw[] form of the instruction. 455 */ updateSwitchInstruction(MSwitchInsn switchInsn, MutatableCode mutatableCode)456 private void updateSwitchInstruction(MSwitchInsn switchInsn, MutatableCode mutatableCode) { 457 // Update the offset to the data instruction 458 MInsn dataTarget = switchInsn.dataTarget; 459 int dataOffset = dataTarget.location - switchInsn.location; 460 ContainsTarget containsTarget = (ContainsTarget) switchInsn.insn.info.format; 461 containsTarget.setTarget(switchInsn.insn, dataOffset); 462 463 int targetsSize = switchInsn.targets.size(); 464 465 int[] keys = switchInsn.keys; 466 int[] targets = new int[targetsSize]; 467 468 // Calculate the new offsets. 469 int targetIdx = 0; 470 for (MInsn target : switchInsn.targets) { 471 targets[targetIdx] = target.location - switchInsn.location; 472 targetIdx++; 473 } 474 475 // Now write the data back to the raw bytes. 476 Instruction dataInsn = switchInsn.dataTarget.insn; 477 478 int rawPtr = 2; 479 480 // Write out the size. 481 RawInsnHelper.writeUnsignedShortToTwoBytes(dataInsn.rawBytes, rawPtr, targetsSize); 482 rawPtr += 2; 483 484 // Write out the keys. 485 if (switchInsn.packed) { 486 // Only write out one key - the first. 487 RawInsnHelper.writeUnsignedIntToFourBytes(dataInsn.rawBytes, rawPtr, keys[0]); 488 rawPtr += 4; 489 } else { 490 // Write out all the keys. 491 for (int i = 0; i < targetsSize; i++) { 492 RawInsnHelper.writeUnsignedIntToFourBytes(dataInsn.rawBytes, rawPtr, keys[i]); 493 rawPtr += 4; 494 } 495 } 496 497 // Write out all the targets. 498 for (int i = 0; i < targetsSize; i++) { 499 RawInsnHelper.writeUnsignedIntToFourBytes(dataInsn.rawBytes, rawPtr, targets[i]); 500 rawPtr += 4; 501 } 502 } 503 504 /** 505 * After mutation, data instructions may no longer be 4-byte aligned. 506 * If this is the case, insert nops to align them all. 507 * This makes a number of assumptions about data currently: 508 * - data is always at the end of method insns 509 * - all data instructions are stored contiguously 510 */ alignDataInstructions(MutatableCode mutatableCode)511 private void alignDataInstructions(MutatableCode mutatableCode) { 512 // Find all the switch data instructions. 513 List<MInsn> dataInsns = new ArrayList<MInsn>(); 514 515 // Update raw sizes of the data instructions as well. 516 for (MInsn mInsn : mutatableCode.getInstructions()) { 517 if (mInsn instanceof MSwitchInsn) { 518 // Update the raw size of the instruction. 519 MSwitchInsn switchInsn = (MSwitchInsn) mInsn; 520 int targetsSize = switchInsn.targets.size(); 521 Instruction dataInsn = switchInsn.dataTarget.insn; 522 if (switchInsn.packed) { 523 dataInsn.rawSize = (targetsSize * 2) + 4; 524 } else { 525 dataInsn.rawSize = (targetsSize * 4) + 2; 526 } 527 dataInsns.add(switchInsn.dataTarget); 528 } else if (mInsn instanceof MInsnWithData) { 529 MInsnWithData insnWithData = 530 (MInsnWithData) mInsn; 531 dataInsns.add(insnWithData.dataTarget); 532 } 533 } 534 535 // Only need to align switch data instructions if there are any! 536 if (!dataInsns.isEmpty()) { 537 538 Log.debug("Found data instructions, checking alignment..."); 539 540 // Sort data_insns by location. 541 Collections.sort(dataInsns, new Comparator<MInsn>() { 542 @Override 543 public int compare(MInsn first, MInsn second) { 544 if (first.location < second.location) { 545 return -1; 546 } else if (first.location > second.location) { 547 return 1; 548 } 549 return 0; 550 } 551 }); 552 553 boolean performedAlignment = false; 554 555 // Go through all the data insns, and insert an alignment nop if they're unaligned. 556 for (MInsn dataInsn : dataInsns) { 557 if (dataInsn.location % 2 != 0) { 558 Log.debug("Aligning data instruction with a nop."); 559 int alignmentNopIdx = mutatableCode.getInstructionIndex(dataInsn); 560 MInsn nop = new MInsn(); 561 nop.insn = new Instruction(); 562 nop.insn.info = Instruction.getOpcodeInfo(Opcode.NOP); 563 mutatableCode.insertInstructionAt(nop, alignmentNopIdx); 564 performedAlignment = true; 565 } 566 } 567 568 if (!performedAlignment) { 569 Log.debug("Alignment okay."); 570 } 571 } 572 } 573 574 /** 575 * Determine if a particular instruction is a branch instruction, based on opcode. 576 */ isInstructionBranch(Instruction insn)577 private boolean isInstructionBranch(Instruction insn) { 578 Opcode opcode = insn.info.opcode; 579 if (Opcode.isBetween(opcode, Opcode.IF_EQ, Opcode.IF_LEZ) 580 || Opcode.isBetween(opcode, Opcode.GOTO, Opcode.GOTO_32)) { 581 return true; 582 } 583 return false; 584 } 585 586 /** 587 * Determine if a particular instruction is a switch instruction, based on opcode. 588 */ isInstructionSwitch(Instruction insn)589 private boolean isInstructionSwitch(Instruction insn) { 590 Opcode opcode = insn.info.opcode; 591 if (Opcode.isBetween(opcode, Opcode.PACKED_SWITCH, Opcode.SPARSE_SWITCH)) { 592 return true; 593 } 594 return false; 595 } 596 isInstructionFillArrayData(Instruction insn)597 private boolean isInstructionFillArrayData(Instruction insn) { 598 return (insn.info.opcode == Opcode.FILL_ARRAY_DATA); 599 } 600 } 601