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       int endLocation = tryItem.startAddr + tryItem.insnCount;
263       mTryBlock.endInsn = insnLocationMap.get(endLocation);
264 
265       // Sanity checks.
266       if (mTryBlock.startInsn == null) {
267         Log.errorAndQuit(String.format(
268             "Couldn't find a mutatable insn at start offset 0x%x",
269             startLocation));
270       }
271       if (mTryBlock.endInsn == null) {
272         Log.errorAndQuit(String.format(
273             "Couldn't find a mutatable insn at end offset 0x%x",
274             endLocation));
275       }
276 
277       // Get the EncodedCatchHandler.
278       int handlerIdx = offsetIndexMap.get(tryItem.handlerOff);
279       EncodedCatchHandler encodedCatchHandler = codeItem.handlers.list[handlerIdx];
280 
281       // Do we have a catch all? If so, associate the MInsn that's there.
282       if (encodedCatchHandler.size <= 0) {
283         mTryBlock.catchAllHandler =
284             insnLocationMap.get(encodedCatchHandler.catchAllAddr);
285         // Sanity check.
286         if (mTryBlock.catchAllHandler == null) {
287           Log.errorAndQuit(
288               String.format("Couldn't find a mutatable insn at catch-all offset 0x%x",
289                   encodedCatchHandler.catchAllAddr));
290         }
291       }
292       // Do we have explicitly-typed handlers? This will remain empty if not.
293       mTryBlock.handlers = new LinkedList<MInsn>();
294 
295       // Associate all the explicitly-typed handlers.
296       for (int i = 0; i < Math.abs(encodedCatchHandler.size); i++) {
297         EncodedTypeAddrPair handler = encodedCatchHandler.handlers[i];
298         MInsn handlerInsn = insnLocationMap.get(handler.addr);
299         // Sanity check.
300         if (handlerInsn == null) {
301           Log.errorAndQuit(String.format(
302               "Couldn't find a mutatable instruction at handler offset 0x%x",
303               handler.addr));
304         }
305         mTryBlock.handlers.add(handlerInsn);
306       }
307 
308       // Now finally add the new MutatableTryBlock into this MutatableCode's list!
309       mutatableCode.mutatableTries.add(mTryBlock);
310     }
311   }
312 
updateTryBlocks(CodeItem codeItem, MutatableCode mutatableCode)313   private void updateTryBlocks(CodeItem codeItem, MutatableCode mutatableCode) {
314 
315     // TODO: Support ability to add extra try blocks/handlers?
316 
317     for (MTryBlock mTryBlock : mutatableCode.mutatableTries) {
318       if (mTryBlock.startInsn.location > mTryBlock.endInsn.location) {
319         // Mutation has put this try block's end insn before its start insn. Fix this.
320         MInsn tempInsn = mTryBlock.startInsn;
321         mTryBlock.startInsn = mTryBlock.endInsn;
322         mTryBlock.endInsn = tempInsn;
323       }
324     }
325 
326     // First, manipulate the try blocks if they overlap.
327     for (int i = 0; i < mutatableCode.mutatableTries.size() - 1; i++) {
328       MTryBlock first = mutatableCode.mutatableTries.get(i);
329       MTryBlock second = mutatableCode.mutatableTries.get(i + 1);
330 
331       // Do they overlap?
332       if (first.endInsn.location > second.startInsn.location) {
333 
334         Log.debug("Found overlap in TryBlocks, moving 2nd TryBlock...");
335         Log.debug("1st TryBlock goes from " + first.startInsn + " to "  + first.endInsn);
336         Log.debug("2nd TryBlock goes from " + second.startInsn + " to "  + second.endInsn);
337 
338         // Find the first instruction that comes after that does not overlap
339         // with the first try block.
340         MInsn newInsn = second.startInsn;
341         int ptr = mutatableCode.getInstructionIndex(newInsn);
342         while (first.endInsn.location > newInsn.location) {
343           ptr++;
344           newInsn = mutatableCode.getInstructionAt(ptr);
345         }
346         second.startInsn = newInsn;
347 
348         Log.debug("Now 2nd TryBlock goes from " + second.startInsn + " to "  + second.endInsn);
349       }
350     }
351 
352     Map<Short,Integer> offsetIndexMap = createTryHandlerOffsetToIndexMap(codeItem);
353 
354     int tryItemIdx = 0;
355     for (MTryBlock mTryBlock : mutatableCode.mutatableTries) {
356       TryItem tryItem = codeItem.tries[tryItemIdx];
357 
358       tryItem.startAddr = mTryBlock.startInsn.location;
359       tryItem.insnCount =
360           (short) (mTryBlock.endInsn.location - mTryBlock.startInsn.location);
361 
362       // Get the EncodedCatchHandler.
363       EncodedCatchHandler encodedCatchHandler =
364           codeItem.handlers.list[offsetIndexMap.get(tryItem.handlerOff)];
365 
366       if (encodedCatchHandler.size <= 0) {
367         encodedCatchHandler.catchAllAddr = mTryBlock.catchAllHandler.location;
368       }
369       for (int i = 0; i < Math.abs(encodedCatchHandler.size); i++) {
370         MInsn handlerInsn = mTryBlock.handlers.get(i);
371         EncodedTypeAddrPair handler = encodedCatchHandler.handlers[i];
372         handler.addr = handlerInsn.location;
373       }
374       tryItemIdx++;
375     }
376   }
377 
378   /**
379    * Given a switch instruction, find the associated data's raw[] form, and update
380    * the targets of the switch instruction to point to the correct instructions.
381    */
readSwitchInstruction(MSwitchInsn switchInsn, Map<Integer,MInsn> insnLocationMap)382   private void readSwitchInstruction(MSwitchInsn switchInsn,
383       Map<Integer,MInsn> insnLocationMap) {
384     // Find the data.
385     ContainsTarget containsTarget = (ContainsTarget) switchInsn.insn.info.format;
386     int dataLocation = switchInsn.location + (int) containsTarget.getTarget(switchInsn.insn);
387     switchInsn.dataTarget = insnLocationMap.get(dataLocation);
388     if (switchInsn.dataTarget == null) {
389       Log.errorAndQuit("Bad offset calculation for data target in switch insn");
390     }
391 
392     // Now read the data.
393     Instruction dataInsn = switchInsn.dataTarget.insn;
394 
395     int rawPtr = 2;
396 
397     int targetsSize = (int) RawInsnHelper.getUnsignedShortFromTwoBytes(dataInsn.rawBytes, rawPtr);
398     rawPtr += 2;
399 
400     int[] keys = new int[targetsSize];
401     int[] targets = new int[targetsSize];
402 
403     if (dataInsn.rawType == 1) {
404       switchInsn.packed = true;
405       // Dealing with a packed-switch.
406       // Read the first key.
407       keys[0] = (int) RawInsnHelper.getUnsignedIntFromFourBytes(dataInsn.rawBytes, rawPtr);
408       rawPtr += 4;
409       // Calculate the rest of the keys.
410       for (int i = 1; i < targetsSize; i++) {
411         keys[i] = keys[i - 1] + 1;
412       }
413     } else if (dataInsn.rawType == 2) {
414       // Dealing with a sparse-switch.
415       // Read all of the keys.
416       for (int i = 0; i < targetsSize; i++) {
417         keys[i] = (int) RawInsnHelper.getUnsignedIntFromFourBytes(dataInsn.rawBytes,
418             rawPtr);
419         rawPtr += 4;
420       }
421     }
422 
423     // Now read the targets.
424     for (int i = 0; i < targetsSize; i++) {
425       targets[i] = (int) RawInsnHelper.getUnsignedIntFromFourBytes(dataInsn.rawBytes,
426           rawPtr);
427       rawPtr += 4;
428     }
429 
430     // Store the keys.
431     switchInsn.keys = keys;
432 
433     // Convert our targets[] offsets into pointers to MInsns.
434     for (int target : targets) {
435       int targetLocation = switchInsn.location + target;
436       MInsn targetInsn = insnLocationMap.get(targetLocation);
437       switchInsn.targets.add(targetInsn);
438       if (targetInsn == null) {
439         Log.errorAndQuit("Bad offset calculation for target in switch insn");
440       }
441     }
442   }
443 
444   /**
445    * Given a mutatable switch instruction, which may have had some of its branch
446    * targets moved, update all the target offsets in the raw[] form of the instruction.
447    */
updateSwitchInstruction(MSwitchInsn switchInsn, MutatableCode mutatableCode)448   private void updateSwitchInstruction(MSwitchInsn switchInsn, MutatableCode mutatableCode) {
449     // Update the offset to the data instruction
450     MInsn dataTarget = switchInsn.dataTarget;
451     int dataOffset = dataTarget.location - switchInsn.location;
452     ContainsTarget containsTarget = (ContainsTarget) switchInsn.insn.info.format;
453     containsTarget.setTarget(switchInsn.insn, dataOffset);
454 
455     int targetsSize = switchInsn.targets.size();
456 
457     int[] keys = switchInsn.keys;
458     int[] targets = new int[targetsSize];
459 
460     // Calculate the new offsets.
461     int targetIdx = 0;
462     for (MInsn target : switchInsn.targets) {
463       targets[targetIdx] = target.location - switchInsn.location;
464       targetIdx++;
465     }
466 
467     // Now write the data back to the raw bytes.
468     Instruction dataInsn = switchInsn.dataTarget.insn;
469 
470     int rawPtr = 2;
471 
472     // Write out the size.
473     RawInsnHelper.writeUnsignedShortToTwoBytes(dataInsn.rawBytes, rawPtr, targetsSize);
474     rawPtr += 2;
475 
476     // Write out the keys.
477     if (switchInsn.packed) {
478       // Only write out one key - the first.
479       RawInsnHelper.writeUnsignedIntToFourBytes(dataInsn.rawBytes, rawPtr, keys[0]);
480       rawPtr += 4;
481     } else {
482       // Write out all the keys.
483       for (int i = 0; i < targetsSize; i++) {
484         RawInsnHelper.writeUnsignedIntToFourBytes(dataInsn.rawBytes, rawPtr, keys[i]);
485         rawPtr += 4;
486       }
487     }
488 
489     // Write out all the targets.
490     for (int i = 0; i < targetsSize; i++) {
491       RawInsnHelper.writeUnsignedIntToFourBytes(dataInsn.rawBytes, rawPtr, targets[i]);
492       rawPtr += 4;
493     }
494   }
495 
496   /**
497    * After mutation, data instructions may no longer be 4-byte aligned.
498    * If this is the case, insert nops to align them all.
499    * This makes a number of assumptions about data currently:
500    * - data is always at the end of method insns
501    * - all data instructions are stored contiguously
502    */
alignDataInstructions(MutatableCode mutatableCode)503   private void alignDataInstructions(MutatableCode mutatableCode) {
504     // Find all the switch data instructions.
505     List<MInsn> dataInsns = new ArrayList<MInsn>();
506 
507     // Update raw sizes of the data instructions as well.
508     for (MInsn mInsn : mutatableCode.getInstructions()) {
509       if (mInsn instanceof MSwitchInsn) {
510         // Update the raw size of the instruction.
511         MSwitchInsn switchInsn = (MSwitchInsn) mInsn;
512         int targetsSize = switchInsn.targets.size();
513         Instruction dataInsn = switchInsn.dataTarget.insn;
514         if (switchInsn.packed) {
515           dataInsn.rawSize = (targetsSize * 2) + 4;
516         } else {
517           dataInsn.rawSize = (targetsSize * 4) + 2;
518         }
519         dataInsns.add(switchInsn.dataTarget);
520       } else if (mInsn instanceof MInsnWithData) {
521         MInsnWithData insnWithData =
522             (MInsnWithData) mInsn;
523         dataInsns.add(insnWithData.dataTarget);
524       }
525     }
526 
527     // Only need to align switch data instructions if there are any!
528     if (!dataInsns.isEmpty()) {
529 
530       Log.debug("Found data instructions, checking alignment...");
531 
532       // Sort data_insns by location.
533       Collections.sort(dataInsns, new Comparator<MInsn>() {
534         @Override
535         public int compare(MInsn first, MInsn second) {
536           if (first.location < second.location) {
537             return -1;
538           } else if (first.location > second.location) {
539             return 1;
540           }
541           return 0;
542         }
543       });
544 
545       boolean performedAlignment = false;
546 
547       // Go through all the data insns, and insert an alignment nop if they're unaligned.
548       for (MInsn dataInsn : dataInsns) {
549         if (dataInsn.location % 2 != 0) {
550           Log.debug("Aligning data instruction with a nop.");
551           int alignmentNopIdx = mutatableCode.getInstructionIndex(dataInsn);
552           MInsn nop = new MInsn();
553           nop.insn = new Instruction();
554           nop.insn.info = Instruction.getOpcodeInfo(Opcode.NOP);
555           mutatableCode.insertInstructionAt(nop, alignmentNopIdx);
556           performedAlignment = true;
557         }
558       }
559 
560       if (!performedAlignment) {
561         Log.debug("Alignment okay.");
562       }
563     }
564   }
565 
566   /**
567    * Determine if a particular instruction is a branch instruction, based on opcode.
568    */
isInstructionBranch(Instruction insn)569   private boolean isInstructionBranch(Instruction insn) {
570     Opcode opcode = insn.info.opcode;
571     if (Opcode.isBetween(opcode, Opcode.IF_EQ, Opcode.IF_LEZ)
572         || Opcode.isBetween(opcode, Opcode.GOTO, Opcode.GOTO_32)) {
573       return true;
574     }
575     return false;
576   }
577 
578   /**
579    * Determine if a particular instruction is a switch instruction, based on opcode.
580    */
isInstructionSwitch(Instruction insn)581   private boolean isInstructionSwitch(Instruction insn) {
582     Opcode opcode = insn.info.opcode;
583     if (Opcode.isBetween(opcode, Opcode.PACKED_SWITCH, Opcode.SPARSE_SWITCH)) {
584       return true;
585     }
586     return false;
587   }
588 
isInstructionFillArrayData(Instruction insn)589   private boolean isInstructionFillArrayData(Instruction insn) {
590     return (insn.info.opcode == Opcode.FILL_ARRAY_DATA);
591   }
592 }
593