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       // Verify start and end instructions.
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         // Verify catch-all handler instruction.
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         // Verify catch handler instruction.
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