• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2013, 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.analysis;
33 
34 import com.google.common.base.Function;
35 import com.google.common.collect.ImmutableList;
36 import com.google.common.collect.Lists;
37 import org.jf.dexlib2.AccessFlags;
38 import org.jf.dexlib2.Opcode;
39 import org.jf.dexlib2.base.reference.BaseMethodReference;
40 import org.jf.dexlib2.iface.*;
41 import org.jf.dexlib2.iface.instruction.*;
42 import org.jf.dexlib2.iface.instruction.formats.*;
43 import org.jf.dexlib2.iface.reference.FieldReference;
44 import org.jf.dexlib2.iface.reference.MethodReference;
45 import org.jf.dexlib2.iface.reference.Reference;
46 import org.jf.dexlib2.iface.reference.TypeReference;
47 import org.jf.dexlib2.immutable.instruction.*;
48 import org.jf.dexlib2.immutable.reference.ImmutableFieldReference;
49 import org.jf.dexlib2.immutable.reference.ImmutableMethodReference;
50 import org.jf.dexlib2.util.MethodUtil;
51 import org.jf.dexlib2.util.ReferenceUtil;
52 import org.jf.dexlib2.util.TypeUtils;
53 import org.jf.dexlib2.writer.util.TryListBuilder;
54 import org.jf.util.BitSetUtils;
55 import org.jf.util.ExceptionWithContext;
56 import org.jf.util.SparseArray;
57 
58 import javax.annotation.Nonnull;
59 import javax.annotation.Nullable;
60 import java.util.BitSet;
61 import java.util.List;
62 
63 /**
64  * The MethodAnalyzer performs several functions. It "analyzes" the instructions and infers the register types
65  * for each register, it can deodex odexed instructions, and it can verify the bytecode. The analysis and verification
66  * are done in two separate passes, because the analysis has to process instructions multiple times in some cases, and
67  * there's no need to perform the verification multiple times, so we wait until the method is fully analyzed and then
68  * verify it.
69  *
70  * Before calling the analyze() method, you must have initialized the ClassPath by calling
71  * ClassPath.InitializeClassPath
72  */
73 public class MethodAnalyzer {
74     @Nonnull private final Method method;
75     @Nonnull private final MethodImplementation methodImpl;
76 
77     private final boolean normalizeVirtualMethods;
78 
79     private final int paramRegisterCount;
80 
81     @Nonnull private final ClassPath classPath;
82     @Nullable private final InlineMethodResolver inlineResolver;
83 
84     // This contains all the AnalyzedInstruction instances, keyed by the code unit address of the instruction
85     @Nonnull private final SparseArray<AnalyzedInstruction> analyzedInstructions =
86             new SparseArray<AnalyzedInstruction>(0);
87 
88     // Which instructions have been analyzed, keyed by instruction index
89     @Nonnull private final BitSet analyzedState;
90 
91     @Nullable private AnalysisException analysisException = null;
92 
93     // This is a dummy instruction that occurs immediately before the first real instruction. We can initialize the
94     // register types for this instruction to the parameter types, in order to have them propagate to all of its
95     // successors, e.g. the first real instruction, the first instructions in any exception handlers covering the first
96     // instruction, etc.
97     private final AnalyzedInstruction startOfMethod;
98 
MethodAnalyzer(@onnull ClassPath classPath, @Nonnull Method method, @Nullable InlineMethodResolver inlineResolver, boolean normalizeVirtualMethods)99     public MethodAnalyzer(@Nonnull ClassPath classPath, @Nonnull Method method,
100                           @Nullable InlineMethodResolver inlineResolver, boolean normalizeVirtualMethods) {
101         this.classPath = classPath;
102         this.inlineResolver = inlineResolver;
103         this.normalizeVirtualMethods = normalizeVirtualMethods;
104 
105         this.method = method;
106 
107         MethodImplementation methodImpl = method.getImplementation();
108         if (methodImpl == null) {
109             throw new IllegalArgumentException("The method has no implementation");
110         }
111 
112         this.methodImpl = methodImpl;
113 
114         // Override AnalyzedInstruction and provide custom implementations of some of the methods, so that we don't
115         // have to handle the case this special case of instruction being null, in the main class
116         startOfMethod = new AnalyzedInstruction(this, new ImmutableInstruction10x(Opcode.NOP), -1, methodImpl.getRegisterCount()) {
117             @Override protected boolean addPredecessor(AnalyzedInstruction predecessor) {
118                 throw new UnsupportedOperationException();
119             }
120 
121             @Override @Nonnull
122             public RegisterType getPredecessorRegisterType(@Nonnull AnalyzedInstruction predecessor, int registerNumber) {
123                 throw new UnsupportedOperationException();
124             }
125         };
126 
127         buildInstructionList();
128 
129         analyzedState = new BitSet(analyzedInstructions.size());
130         paramRegisterCount = MethodUtil.getParameterRegisterCount(method);
131         analyze();
132     }
133 
134     @Nonnull
getClassPath()135     public ClassPath getClassPath() {
136         return classPath;
137     }
138 
analyze()139     private void analyze() {
140         Method method = this.method;
141         MethodImplementation methodImpl = this.methodImpl;
142 
143         int totalRegisters = methodImpl.getRegisterCount();
144         int parameterRegisters = paramRegisterCount;
145 
146         int nonParameterRegisters = totalRegisters - parameterRegisters;
147 
148         //if this isn't a static method, determine which register is the "this" register and set the type to the
149         //current class
150         if (!MethodUtil.isStatic(method)) {
151             int thisRegister = totalRegisters - parameterRegisters;
152 
153             //if this is a constructor, then set the "this" register to an uninitialized reference of the current class
154             if (MethodUtil.isConstructor(method)) {
155                 setPostRegisterTypeAndPropagateChanges(startOfMethod, thisRegister,
156                         RegisterType.getRegisterType(RegisterType.UNINIT_THIS,
157                                 classPath.getClass(method.getDefiningClass())));
158             } else {
159                 setPostRegisterTypeAndPropagateChanges(startOfMethod, thisRegister,
160                         RegisterType.getRegisterType(RegisterType.REFERENCE,
161                                 classPath.getClass(method.getDefiningClass())));
162             }
163 
164             propagateParameterTypes(totalRegisters-parameterRegisters+1);
165         } else {
166             propagateParameterTypes(totalRegisters-parameterRegisters);
167         }
168 
169         RegisterType uninit = RegisterType.getRegisterType(RegisterType.UNINIT, null);
170         for (int i=0; i<nonParameterRegisters; i++) {
171             setPostRegisterTypeAndPropagateChanges(startOfMethod, i, uninit);
172         }
173 
174         BitSet instructionsToAnalyze = new BitSet(analyzedInstructions.size());
175 
176         //make sure all of the "first instructions" are marked for processing
177         for (AnalyzedInstruction successor: startOfMethod.successors) {
178             instructionsToAnalyze.set(successor.instructionIndex);
179         }
180 
181         BitSet undeodexedInstructions = new BitSet(analyzedInstructions.size());
182 
183         do {
184             boolean didSomething = false;
185 
186             while (!instructionsToAnalyze.isEmpty()) {
187                 for(int i=instructionsToAnalyze.nextSetBit(0); i>=0; i=instructionsToAnalyze.nextSetBit(i+1)) {
188                     instructionsToAnalyze.clear(i);
189                     if (analyzedState.get(i)) {
190                         continue;
191                     }
192                     AnalyzedInstruction instructionToAnalyze = analyzedInstructions.valueAt(i);
193                     try {
194                         if (instructionToAnalyze.originalInstruction.getOpcode().odexOnly()) {
195                             //if we had deodexed an odex instruction in a previous pass, we might have more specific
196                             //register information now, so let's restore the original odexed instruction and
197                             //re-deodex it
198                             instructionToAnalyze.restoreOdexedInstruction();
199                         }
200 
201                         if (!analyzeInstruction(instructionToAnalyze)) {
202                             undeodexedInstructions.set(i);
203                             continue;
204                         } else {
205                             didSomething = true;
206                             undeodexedInstructions.clear(i);
207                         }
208                     } catch (AnalysisException ex) {
209                         this.analysisException = ex;
210                         int codeAddress = getInstructionAddress(instructionToAnalyze);
211                         ex.codeAddress = codeAddress;
212                         ex.addContext(String.format("opcode: %s", instructionToAnalyze.instruction.getOpcode().name));
213                         ex.addContext(String.format("code address: %d", codeAddress));
214                         ex.addContext(String.format("method: %s", ReferenceUtil.getReferenceString(method)));
215                         break;
216                     }
217 
218                     analyzedState.set(instructionToAnalyze.getInstructionIndex());
219 
220                     for (AnalyzedInstruction successor: instructionToAnalyze.successors) {
221                         instructionsToAnalyze.set(successor.getInstructionIndex());
222                     }
223                 }
224                 if (analysisException != null) {
225                     break;
226                 }
227             }
228 
229             if (!didSomething) {
230                 break;
231             }
232 
233             if (!undeodexedInstructions.isEmpty()) {
234                 for (int i=undeodexedInstructions.nextSetBit(0); i>=0; i=undeodexedInstructions.nextSetBit(i+1)) {
235                     instructionsToAnalyze.set(i);
236                 }
237             }
238         } while (true);
239 
240         //Now, go through and fix up any unresolvable odex instructions. These are usually odex instructions
241         //that operate on a null register, and thus always throw an NPE. They can also be any sort of odex instruction
242         //that occurs after an unresolvable odex instruction. We deodex if possible, or replace with an
243         //UnresolvableOdexInstruction
244         for (int i=0; i< analyzedInstructions.size(); i++) {
245             AnalyzedInstruction analyzedInstruction = analyzedInstructions.valueAt(i);
246 
247             Instruction instruction = analyzedInstruction.getInstruction();
248 
249             if (instruction.getOpcode().odexOnly()) {
250                 int objectRegisterNumber;
251                 switch (instruction.getOpcode().format) {
252                     case Format10x:
253                         analyzeOdexReturnVoid(analyzedInstruction, false);
254                         continue;
255                     case Format21c:
256                     case Format22c:
257                         analyzePutGetVolatile(analyzedInstruction, false);
258                         continue;
259                     case Format35c:
260                         analyzeInvokeDirectEmpty(analyzedInstruction, false);
261                         continue;
262                     case Format3rc:
263                         analyzeInvokeObjectInitRange(analyzedInstruction, false);
264                         continue;
265                     case Format22cs:
266                         objectRegisterNumber = ((Instruction22cs)instruction).getRegisterB();
267                         break;
268                     case Format35mi:
269                     case Format35ms:
270                         objectRegisterNumber = ((FiveRegisterInstruction)instruction).getRegisterC();
271                         break;
272                     case Format3rmi:
273                     case Format3rms:
274                         objectRegisterNumber = ((RegisterRangeInstruction)instruction).getStartRegister();
275                         break;
276                     default:
277                         continue;
278                 }
279 
280                 analyzedInstruction.setDeodexedInstruction(
281                         new UnresolvedOdexInstruction(instruction, objectRegisterNumber));
282             }
283         }
284     }
285 
propagateParameterTypes(int parameterStartRegister)286     private void propagateParameterTypes(int parameterStartRegister) {
287         int i=0;
288         for (MethodParameter parameter: method.getParameters()) {
289             if (TypeUtils.isWideType(parameter)) {
290                 setPostRegisterTypeAndPropagateChanges(startOfMethod, parameterStartRegister + i++,
291                         RegisterType.getWideRegisterType(parameter, true));
292                 setPostRegisterTypeAndPropagateChanges(startOfMethod, parameterStartRegister + i++,
293                         RegisterType.getWideRegisterType(parameter, false));
294             } else {
295                 setPostRegisterTypeAndPropagateChanges(startOfMethod, parameterStartRegister + i++,
296                         RegisterType.getRegisterType(classPath, parameter));
297             }
298         }
299     }
300 
getAnalyzedInstructions()301     public List<AnalyzedInstruction> getAnalyzedInstructions() {
302         return analyzedInstructions.getValues();
303     }
304 
getInstructions()305     public List<Instruction> getInstructions() {
306         return Lists.transform(analyzedInstructions.getValues(), new Function<AnalyzedInstruction, Instruction>() {
307             @Nullable @Override public Instruction apply(@Nullable AnalyzedInstruction input) {
308                 if (input == null) {
309                     return null;
310                 }
311                 return input.instruction;
312             }
313         });
314     }
315 
316     @Nullable
317     public AnalysisException getAnalysisException() {
318         return analysisException;
319     }
320 
321     public int getParamRegisterCount() {
322         return paramRegisterCount;
323     }
324 
325     public int getInstructionAddress(@Nonnull AnalyzedInstruction instruction) {
326         return analyzedInstructions.keyAt(instruction.instructionIndex);
327     }
328 
329     private void setDestinationRegisterTypeAndPropagateChanges(@Nonnull AnalyzedInstruction analyzedInstruction,
330                                                                @Nonnull RegisterType registerType) {
331         setPostRegisterTypeAndPropagateChanges(analyzedInstruction, analyzedInstruction.getDestinationRegister(),
332                 registerType);
333     }
334 
335     private void propagateChanges(@Nonnull BitSet changedInstructions, int registerNumber, boolean override) {
336         //Using a for loop inside the while loop optimizes for the common case of the successors of an instruction
337         //occurring after the instruction. Any successors that occur prior to the instruction will be picked up on
338         //the next iteration of the while loop.
339         //This could also be done recursively, but in large methods it would likely cause very deep recursion.
340         while (!changedInstructions.isEmpty()) {
341             for (int instructionIndex=changedInstructions.nextSetBit(0);
342                  instructionIndex>=0;
343                  instructionIndex=changedInstructions.nextSetBit(instructionIndex+1)) {
344 
345                 changedInstructions.clear(instructionIndex);
346 
347                 propagateRegisterToSuccessors(analyzedInstructions.valueAt(instructionIndex), registerNumber,
348                         changedInstructions, override);
349             }
350         }
351     }
352 
353     private void overridePredecessorRegisterTypeAndPropagateChanges(
354             @Nonnull AnalyzedInstruction analyzedInstruction, @Nonnull AnalyzedInstruction predecessor,
355             int registerNumber, @Nonnull RegisterType registerType) {
356 
357         BitSet changedInstructions = new BitSet(analyzedInstructions.size());
358 
359         if (!analyzedInstruction.overridePredecessorRegisterType(
360                 predecessor, registerNumber, registerType, analyzedState)) {
361             return;
362         }
363         changedInstructions.set(analyzedInstruction.instructionIndex);
364 
365         propagateChanges(changedInstructions, registerNumber, true);
366 
367         if (registerType.category == RegisterType.LONG_LO) {
368             checkWidePair(registerNumber, analyzedInstruction);
369             overridePredecessorRegisterTypeAndPropagateChanges(analyzedInstruction, predecessor, registerNumber + 1,
370                     RegisterType.LONG_HI_TYPE);
371         } else if (registerType.category == RegisterType.DOUBLE_LO) {
372             checkWidePair(registerNumber, analyzedInstruction);
373             overridePredecessorRegisterTypeAndPropagateChanges(analyzedInstruction, predecessor, registerNumber + 1,
374                     RegisterType.DOUBLE_HI_TYPE);
375         }
376     }
377 
378     private void initializeRefAndPropagateChanges(@Nonnull AnalyzedInstruction analyzedInstruction,
379                                                   int registerNumber, @Nonnull RegisterType registerType) {
380 
381         BitSet changedInstructions = new BitSet(analyzedInstructions.size());
382 
383         if (!analyzedInstruction.setPostRegisterType(registerNumber, registerType)) {
384             return;
385         }
386 
387         propagateRegisterToSuccessors(analyzedInstruction, registerNumber, changedInstructions, false);
388 
389         propagateChanges(changedInstructions, registerNumber, false);
390 
391         if (registerType.category == RegisterType.LONG_LO) {
392             checkWidePair(registerNumber, analyzedInstruction);
393             setPostRegisterTypeAndPropagateChanges(analyzedInstruction, registerNumber+1, RegisterType.LONG_HI_TYPE);
394         } else if (registerType.category == RegisterType.DOUBLE_LO) {
395             checkWidePair(registerNumber, analyzedInstruction);
396             setPostRegisterTypeAndPropagateChanges(analyzedInstruction, registerNumber+1, RegisterType.DOUBLE_HI_TYPE);
397         }
398     }
399 
400     private void setPostRegisterTypeAndPropagateChanges(@Nonnull AnalyzedInstruction analyzedInstruction,
401                                                         int registerNumber, @Nonnull RegisterType registerType) {
402 
403         BitSet changedInstructions = new BitSet(analyzedInstructions.size());
404 
405         if (!analyzedInstruction.setPostRegisterType(registerNumber, registerType)) {
406             return;
407         }
408 
409         propagateRegisterToSuccessors(analyzedInstruction, registerNumber, changedInstructions, false);
410 
411         propagateChanges(changedInstructions, registerNumber, false);
412 
413         if (registerType.category == RegisterType.LONG_LO) {
414             checkWidePair(registerNumber, analyzedInstruction);
415             setPostRegisterTypeAndPropagateChanges(analyzedInstruction, registerNumber+1, RegisterType.LONG_HI_TYPE);
416         } else if (registerType.category == RegisterType.DOUBLE_LO) {
417             checkWidePair(registerNumber, analyzedInstruction);
418             setPostRegisterTypeAndPropagateChanges(analyzedInstruction, registerNumber+1, RegisterType.DOUBLE_HI_TYPE);
419         }
420     }
421 
422     private void propagateRegisterToSuccessors(@Nonnull AnalyzedInstruction instruction, int registerNumber,
423                                                @Nonnull BitSet changedInstructions, boolean override) {
424         RegisterType postRegisterType = instruction.getPostInstructionRegisterType(registerNumber);
425         for (AnalyzedInstruction successor: instruction.successors) {
426             if (successor.mergeRegister(registerNumber, postRegisterType, analyzedState, override)) {
427                 changedInstructions.set(successor.instructionIndex);
428             }
429         }
430     }
431 
432     private void buildInstructionList() {
433         int registerCount = methodImpl.getRegisterCount();
434 
435         ImmutableList<Instruction> instructions = ImmutableList.copyOf(methodImpl.getInstructions());
436 
437         analyzedInstructions.ensureCapacity(instructions.size());
438 
439         //first, create all the instructions and populate the instructionAddresses array
440         int currentCodeAddress = 0;
441         for (int i=0; i<instructions.size(); i++) {
442             Instruction instruction = instructions.get(i);
443             analyzedInstructions.append(currentCodeAddress,
444                     new AnalyzedInstruction(this, instruction, i, registerCount));
445             assert analyzedInstructions.indexOfKey(currentCodeAddress) == i;
446             currentCodeAddress += instruction.getCodeUnits();
447         }
448 
449         //next, populate the exceptionHandlers array. The array item for each instruction that can throw an exception
450         //and is covered by a try block should be set to a list of the first instructions of each exception handler
451         //for the try block covering the instruction
452         List<? extends TryBlock<? extends ExceptionHandler>> tries = methodImpl.getTryBlocks();
453         tries = TryListBuilder.massageTryBlocks(tries);
454         int triesIndex = 0;
455         TryBlock currentTry = null;
456         AnalyzedInstruction[] currentExceptionHandlers = null;
457         AnalyzedInstruction[][] exceptionHandlers = new AnalyzedInstruction[instructions.size()][];
458 
459         if (tries != null) {
460             for (int i=0; i< analyzedInstructions.size(); i++) {
461                 AnalyzedInstruction instruction = analyzedInstructions.valueAt(i);
462                 Opcode instructionOpcode = instruction.instruction.getOpcode();
463                 currentCodeAddress = getInstructionAddress(instruction);
464 
465                 //check if we have gone past the end of the current try
466                 if (currentTry != null) {
467                     if (currentTry.getStartCodeAddress() + currentTry.getCodeUnitCount()  <= currentCodeAddress) {
468                         currentTry = null;
469                         triesIndex++;
470                     }
471                 }
472 
473                 //check if the next try is applicable yet
474                 if (currentTry == null && triesIndex < tries.size()) {
475                     TryBlock<? extends ExceptionHandler> tryBlock = tries.get(triesIndex);
476                     if (tryBlock.getStartCodeAddress() <= currentCodeAddress) {
477                         assert(tryBlock.getStartCodeAddress() + tryBlock.getCodeUnitCount() > currentCodeAddress);
478 
479                         currentTry = tryBlock;
480 
481                         currentExceptionHandlers = buildExceptionHandlerArray(tryBlock);
482                     }
483                 }
484 
485                 //if we're inside a try block, and the instruction can throw an exception, then add the exception handlers
486                 //for the current instruction
487                 if (currentTry != null && instructionOpcode.canThrow()) {
488                     exceptionHandlers[i] = currentExceptionHandlers;
489                 }
490             }
491         }
492 
493         //finally, populate the successors and predecessors for each instruction. We start at the fake "StartOfMethod"
494         //instruction and follow the execution path. Any unreachable code won't have any predecessors or successors,
495         //and no reachable code will have an unreachable predessor or successor
496         assert analyzedInstructions.size() > 0;
497         BitSet instructionsToProcess = new BitSet(instructions.size());
498 
499         addPredecessorSuccessor(startOfMethod, analyzedInstructions.valueAt(0), exceptionHandlers, instructionsToProcess);
500         while (!instructionsToProcess.isEmpty()) {
501             int currentInstructionIndex = instructionsToProcess.nextSetBit(0);
502             instructionsToProcess.clear(currentInstructionIndex);
503 
504             AnalyzedInstruction instruction = analyzedInstructions.valueAt(currentInstructionIndex);
505             Opcode instructionOpcode = instruction.instruction.getOpcode();
506             int instructionCodeAddress = getInstructionAddress(instruction);
507 
508             if (instruction.instruction.getOpcode().canContinue()) {
509                 if (currentInstructionIndex == analyzedInstructions.size() - 1) {
510                     throw new AnalysisException("Execution can continue past the last instruction");
511                 }
512 
513                 AnalyzedInstruction nextInstruction = analyzedInstructions.valueAt(currentInstructionIndex+1);
514                 addPredecessorSuccessor(instruction, nextInstruction, exceptionHandlers, instructionsToProcess);
515             }
516 
517             if (instruction.instruction instanceof OffsetInstruction) {
518                 OffsetInstruction offsetInstruction = (OffsetInstruction)instruction.instruction;
519 
520                 if (instructionOpcode == Opcode.PACKED_SWITCH || instructionOpcode == Opcode.SPARSE_SWITCH) {
521                     AnalyzedInstruction analyzedSwitchPayload = analyzedInstructions.get(
522                             instructionCodeAddress + offsetInstruction.getCodeOffset());
523                     if (analyzedSwitchPayload == null) {
524                         throw new AnalysisException("Invalid switch payload offset");
525                     }
526                     SwitchPayload switchPayload = (SwitchPayload)analyzedSwitchPayload.instruction;
527 
528                     for (SwitchElement switchElement: switchPayload.getSwitchElements()) {
529                         AnalyzedInstruction targetInstruction = analyzedInstructions.get(instructionCodeAddress +
530                                 switchElement.getOffset());
531                         if (targetInstruction == null) {
532                             throw new AnalysisException("Invalid switch target offset");
533                         }
534 
535                         addPredecessorSuccessor(instruction, targetInstruction, exceptionHandlers,
536                                 instructionsToProcess);
537                     }
538                 } else if (instructionOpcode != Opcode.FILL_ARRAY_DATA) {
539                     int targetAddressOffset = offsetInstruction.getCodeOffset();
540                     AnalyzedInstruction targetInstruction = analyzedInstructions.get(instructionCodeAddress +
541                             targetAddressOffset);
542                     addPredecessorSuccessor(instruction, targetInstruction, exceptionHandlers, instructionsToProcess);
543                 }
544             }
545         }
546     }
547 
548     private void addPredecessorSuccessor(@Nonnull AnalyzedInstruction predecessor,
549                                          @Nonnull AnalyzedInstruction successor,
550                                          @Nonnull AnalyzedInstruction[][] exceptionHandlers,
551                                          @Nonnull BitSet instructionsToProcess) {
552         addPredecessorSuccessor(predecessor, successor, exceptionHandlers, instructionsToProcess, false);
553     }
554 
555     private void addPredecessorSuccessor(@Nonnull AnalyzedInstruction predecessor,
556                                          @Nonnull AnalyzedInstruction successor,
557                                          @Nonnull AnalyzedInstruction[][] exceptionHandlers,
558                                          @Nonnull BitSet instructionsToProcess, boolean allowMoveException) {
559 
560         if (!allowMoveException && successor.instruction.getOpcode() == Opcode.MOVE_EXCEPTION) {
561             throw new AnalysisException("Execution can pass from the " + predecessor.instruction.getOpcode().name +
562                     " instruction at code address 0x" + Integer.toHexString(getInstructionAddress(predecessor)) +
563                     " to the move-exception instruction at address 0x" +
564                     Integer.toHexString(getInstructionAddress(successor)));
565         }
566 
567         if (!successor.addPredecessor(predecessor)) {
568             return;
569         }
570 
571         predecessor.addSuccessor(successor);
572         instructionsToProcess.set(successor.getInstructionIndex());
573 
574 
575         //if the successor can throw an instruction, then we need to add the exception handlers as additional
576         //successors to the predecessor (and then apply this same logic recursively if needed)
577         //Technically, we should handle the monitor-exit instruction as a special case. The exception is actually
578         //thrown *after* the instruction executes, instead of "before" the instruction executes, lke for any other
579         //instruction. But since it doesn't modify any registers, we can treat it like any other instruction.
580         AnalyzedInstruction[] exceptionHandlersForSuccessor = exceptionHandlers[successor.instructionIndex];
581         if (exceptionHandlersForSuccessor != null) {
582             //the item for this instruction in exceptionHandlersForSuccessor should only be set if this instruction
583             //can throw an exception
584             assert successor.instruction.getOpcode().canThrow();
585 
586             for (AnalyzedInstruction exceptionHandler: exceptionHandlersForSuccessor) {
587                 addPredecessorSuccessor(predecessor, exceptionHandler, exceptionHandlers, instructionsToProcess, true);
588             }
589         }
590     }
591 
592     @Nonnull
593     private AnalyzedInstruction[] buildExceptionHandlerArray(@Nonnull TryBlock<? extends ExceptionHandler> tryBlock) {
594         List<? extends ExceptionHandler> exceptionHandlers = tryBlock.getExceptionHandlers();
595 
596         AnalyzedInstruction[] handlerInstructions = new AnalyzedInstruction[exceptionHandlers.size()];
597         for (int i=0; i<exceptionHandlers.size(); i++) {
598             handlerInstructions[i] = analyzedInstructions.get(exceptionHandlers.get(i).getHandlerCodeAddress());
599         }
600 
601         return handlerInstructions;
602     }
603 
604     /**
605      * @return false if analyzedInstruction is an odex instruction that couldn't be deodexed, due to its
606      * object register being null
607      */
608     private boolean analyzeInstruction(@Nonnull AnalyzedInstruction analyzedInstruction) {
609         Instruction instruction = analyzedInstruction.instruction;
610 
611         switch (instruction.getOpcode()) {
612             case NOP:
613                 return true;
614             case MOVE:
615             case MOVE_FROM16:
616             case MOVE_16:
617             case MOVE_WIDE:
618             case MOVE_WIDE_FROM16:
619             case MOVE_WIDE_16:
620             case MOVE_OBJECT:
621             case MOVE_OBJECT_FROM16:
622             case MOVE_OBJECT_16:
623                 analyzeMove(analyzedInstruction);
624                 return true;
625             case MOVE_RESULT:
626             case MOVE_RESULT_WIDE:
627             case MOVE_RESULT_OBJECT:
628                 analyzeMoveResult(analyzedInstruction);
629                 return true;
630             case MOVE_EXCEPTION:
631                 analyzeMoveException(analyzedInstruction);
632                 return true;
633             case RETURN_VOID:
634             case RETURN:
635             case RETURN_WIDE:
636             case RETURN_OBJECT:
637                 return true;
638             case RETURN_VOID_BARRIER:
639             case RETURN_VOID_NO_BARRIER:
640                 analyzeOdexReturnVoid(analyzedInstruction);
641                 return true;
642             case CONST_4:
643             case CONST_16:
644             case CONST:
645             case CONST_HIGH16:
646                 analyzeConst(analyzedInstruction);
647                 return true;
648             case CONST_WIDE_16:
649             case CONST_WIDE_32:
650             case CONST_WIDE:
651             case CONST_WIDE_HIGH16:
652                 analyzeWideConst(analyzedInstruction);
653                 return true;
654             case CONST_STRING:
655             case CONST_STRING_JUMBO:
656                 analyzeConstString(analyzedInstruction);
657                 return true;
658             case CONST_CLASS:
659                 analyzeConstClass(analyzedInstruction);
660                 return true;
661             case MONITOR_ENTER:
662             case MONITOR_EXIT:
663                 return true;
664             case CHECK_CAST:
665                 analyzeCheckCast(analyzedInstruction);
666                 return true;
667             case INSTANCE_OF:
668                 analyzeInstanceOf(analyzedInstruction);
669                 return true;
670             case ARRAY_LENGTH:
671                 analyzeArrayLength(analyzedInstruction);
672                 return true;
673             case NEW_INSTANCE:
674                 analyzeNewInstance(analyzedInstruction);
675                 return true;
676             case NEW_ARRAY:
677                 analyzeNewArray(analyzedInstruction);
678                 return true;
679             case FILLED_NEW_ARRAY:
680             case FILLED_NEW_ARRAY_RANGE:
681                 return true;
682             case FILL_ARRAY_DATA:
683                 return true;
684             case THROW:
685             case GOTO:
686             case GOTO_16:
687             case GOTO_32:
688                 return true;
689             case PACKED_SWITCH:
690             case SPARSE_SWITCH:
691                 return true;
692             case CMPL_FLOAT:
693             case CMPG_FLOAT:
694             case CMPL_DOUBLE:
695             case CMPG_DOUBLE:
696             case CMP_LONG:
697                 analyzeFloatWideCmp(analyzedInstruction);
698                 return true;
699             case IF_EQ:
700             case IF_NE:
701             case IF_LT:
702             case IF_GE:
703             case IF_GT:
704             case IF_LE:
705             case IF_LTZ:
706             case IF_GEZ:
707             case IF_GTZ:
708             case IF_LEZ:
709                 return true;
710             case IF_EQZ:
711             case IF_NEZ:
712                 analyzeIfEqzNez(analyzedInstruction);
713                 return true;
714             case AGET:
715                 analyze32BitPrimitiveAget(analyzedInstruction, RegisterType.INTEGER_TYPE);
716                 return true;
717             case AGET_BOOLEAN:
718                 analyze32BitPrimitiveAget(analyzedInstruction, RegisterType.BOOLEAN_TYPE);
719                 return true;
720             case AGET_BYTE:
721                 analyze32BitPrimitiveAget(analyzedInstruction, RegisterType.BYTE_TYPE);
722                 return true;
723             case AGET_CHAR:
724                 analyze32BitPrimitiveAget(analyzedInstruction, RegisterType.CHAR_TYPE);
725                 return true;
726             case AGET_SHORT:
727                 analyze32BitPrimitiveAget(analyzedInstruction, RegisterType.SHORT_TYPE);
728                 return true;
729             case AGET_WIDE:
730                 analyzeAgetWide(analyzedInstruction);
731                 return true;
732             case AGET_OBJECT:
733                 analyzeAgetObject(analyzedInstruction);
734                 return true;
735             case APUT:
736             case APUT_BOOLEAN:
737             case APUT_BYTE:
738             case APUT_CHAR:
739             case APUT_SHORT:
740             case APUT_WIDE:
741             case APUT_OBJECT:
742                 return true;
743             case IGET:
744                 analyze32BitPrimitiveIgetSget(analyzedInstruction, RegisterType.INTEGER_TYPE);
745                 return true;
746             case IGET_BOOLEAN:
747                 analyze32BitPrimitiveIgetSget(analyzedInstruction, RegisterType.BOOLEAN_TYPE);
748                 return true;
749             case IGET_BYTE:
750                 analyze32BitPrimitiveIgetSget(analyzedInstruction, RegisterType.BYTE_TYPE);
751                 return true;
752             case IGET_CHAR:
753                 analyze32BitPrimitiveIgetSget(analyzedInstruction, RegisterType.CHAR_TYPE);
754                 return true;
755             case IGET_SHORT:
756                 analyze32BitPrimitiveIgetSget(analyzedInstruction, RegisterType.SHORT_TYPE);
757                 return true;
758             case IGET_WIDE:
759             case IGET_OBJECT:
760                 analyzeIgetSgetWideObject(analyzedInstruction);
761                 return true;
762             case IPUT:
763             case IPUT_BOOLEAN:
764             case IPUT_BYTE:
765             case IPUT_CHAR:
766             case IPUT_SHORT:
767             case IPUT_WIDE:
768             case IPUT_OBJECT:
769                 return true;
770             case SGET:
771                 analyze32BitPrimitiveIgetSget(analyzedInstruction, RegisterType.INTEGER_TYPE);
772                 return true;
773             case SGET_BOOLEAN:
774                 analyze32BitPrimitiveIgetSget(analyzedInstruction, RegisterType.BOOLEAN_TYPE);
775                 return true;
776             case SGET_BYTE:
777                 analyze32BitPrimitiveIgetSget(analyzedInstruction, RegisterType.BYTE_TYPE);
778                 return true;
779             case SGET_CHAR:
780                 analyze32BitPrimitiveIgetSget(analyzedInstruction, RegisterType.CHAR_TYPE);
781                 return true;
782             case SGET_SHORT:
783                 analyze32BitPrimitiveIgetSget(analyzedInstruction, RegisterType.SHORT_TYPE);
784                 return true;
785             case SGET_WIDE:
786             case SGET_OBJECT:
787                 analyzeIgetSgetWideObject(analyzedInstruction);
788                 return true;
789             case SPUT:
790             case SPUT_BOOLEAN:
791             case SPUT_BYTE:
792             case SPUT_CHAR:
793             case SPUT_SHORT:
794             case SPUT_WIDE:
795             case SPUT_OBJECT:
796                 return true;
797             case INVOKE_VIRTUAL:
798                 analyzeInvokeVirtual(analyzedInstruction, false);
799                 return true;
800             case INVOKE_SUPER:
801                 analyzeInvokeVirtual(analyzedInstruction, false);
802                 return true;
803             case INVOKE_DIRECT:
804                 analyzeInvokeDirect(analyzedInstruction);
805                 return true;
806             case INVOKE_STATIC:
807                 return true;
808             case INVOKE_INTERFACE:
809                 // TODO: normalize interfaces
810                 return true;
811             case INVOKE_VIRTUAL_RANGE:
812                 analyzeInvokeVirtual(analyzedInstruction, true);
813                 return true;
814             case INVOKE_SUPER_RANGE:
815                 analyzeInvokeVirtual(analyzedInstruction, true);
816                 return true;
817             case INVOKE_DIRECT_RANGE:
818                 analyzeInvokeDirectRange(analyzedInstruction);
819                 return true;
820             case INVOKE_STATIC_RANGE:
821                 return true;
822             case INVOKE_INTERFACE_RANGE:
823                 // TODO: normalize interfaces
824                 return true;
825             case NEG_INT:
826             case NOT_INT:
827                 analyzeUnaryOp(analyzedInstruction, RegisterType.INTEGER_TYPE);
828                 return true;
829             case NEG_LONG:
830             case NOT_LONG:
831                 analyzeUnaryOp(analyzedInstruction, RegisterType.LONG_LO_TYPE);
832                 return true;
833             case NEG_FLOAT:
834                 analyzeUnaryOp(analyzedInstruction, RegisterType.FLOAT_TYPE);
835                 return true;
836             case NEG_DOUBLE:
837                 analyzeUnaryOp(analyzedInstruction, RegisterType.DOUBLE_LO_TYPE);
838                 return true;
839             case INT_TO_LONG:
840                 analyzeUnaryOp(analyzedInstruction, RegisterType.LONG_LO_TYPE);
841                 return true;
842             case INT_TO_FLOAT:
843                 analyzeUnaryOp(analyzedInstruction, RegisterType.FLOAT_TYPE);
844                 return true;
845             case INT_TO_DOUBLE:
846                 analyzeUnaryOp(analyzedInstruction, RegisterType.DOUBLE_LO_TYPE);
847                 return true;
848             case LONG_TO_INT:
849             case DOUBLE_TO_INT:
850                 analyzeUnaryOp(analyzedInstruction, RegisterType.INTEGER_TYPE);
851                 return true;
852             case LONG_TO_FLOAT:
853             case DOUBLE_TO_FLOAT:
854                 analyzeUnaryOp(analyzedInstruction, RegisterType.FLOAT_TYPE);
855                 return true;
856             case LONG_TO_DOUBLE:
857                 analyzeUnaryOp(analyzedInstruction, RegisterType.DOUBLE_LO_TYPE);
858                 return true;
859             case FLOAT_TO_INT:
860                 analyzeUnaryOp(analyzedInstruction, RegisterType.INTEGER_TYPE);
861                 return true;
862             case FLOAT_TO_LONG:
863                 analyzeUnaryOp(analyzedInstruction, RegisterType.LONG_LO_TYPE);
864                 return true;
865             case FLOAT_TO_DOUBLE:
866                 analyzeUnaryOp(analyzedInstruction, RegisterType.DOUBLE_LO_TYPE);
867                 return true;
868             case DOUBLE_TO_LONG:
869                 analyzeUnaryOp(analyzedInstruction, RegisterType.LONG_LO_TYPE);
870                 return true;
871             case INT_TO_BYTE:
872                 analyzeUnaryOp(analyzedInstruction, RegisterType.BYTE_TYPE);
873                 return true;
874             case INT_TO_CHAR:
875                 analyzeUnaryOp(analyzedInstruction, RegisterType.CHAR_TYPE);
876                 return true;
877             case INT_TO_SHORT:
878                 analyzeUnaryOp(analyzedInstruction, RegisterType.SHORT_TYPE);
879                 return true;
880             case ADD_INT:
881             case SUB_INT:
882             case MUL_INT:
883             case DIV_INT:
884             case REM_INT:
885             case SHL_INT:
886             case SHR_INT:
887             case USHR_INT:
888                 analyzeBinaryOp(analyzedInstruction, RegisterType.INTEGER_TYPE, false);
889                 return true;
890             case AND_INT:
891             case OR_INT:
892             case XOR_INT:
893                 analyzeBinaryOp(analyzedInstruction, RegisterType.INTEGER_TYPE, true);
894                 return true;
895             case ADD_LONG:
896             case SUB_LONG:
897             case MUL_LONG:
898             case DIV_LONG:
899             case REM_LONG:
900             case AND_LONG:
901             case OR_LONG:
902             case XOR_LONG:
903             case SHL_LONG:
904             case SHR_LONG:
905             case USHR_LONG:
906                 analyzeBinaryOp(analyzedInstruction, RegisterType.LONG_LO_TYPE, false);
907                 return true;
908             case ADD_FLOAT:
909             case SUB_FLOAT:
910             case MUL_FLOAT:
911             case DIV_FLOAT:
912             case REM_FLOAT:
913                 analyzeBinaryOp(analyzedInstruction, RegisterType.FLOAT_TYPE, false);
914                 return true;
915             case ADD_DOUBLE:
916             case SUB_DOUBLE:
917             case MUL_DOUBLE:
918             case DIV_DOUBLE:
919             case REM_DOUBLE:
920                 analyzeBinaryOp(analyzedInstruction, RegisterType.DOUBLE_LO_TYPE, false);
921                 return true;
922             case ADD_INT_2ADDR:
923             case SUB_INT_2ADDR:
924             case MUL_INT_2ADDR:
925             case DIV_INT_2ADDR:
926             case REM_INT_2ADDR:
927             case SHL_INT_2ADDR:
928             case SHR_INT_2ADDR:
929             case USHR_INT_2ADDR:
930                 analyzeBinary2AddrOp(analyzedInstruction, RegisterType.INTEGER_TYPE, false);
931                 return true;
932             case AND_INT_2ADDR:
933             case OR_INT_2ADDR:
934             case XOR_INT_2ADDR:
935                 analyzeBinary2AddrOp(analyzedInstruction, RegisterType.INTEGER_TYPE, true);
936                 return true;
937             case ADD_LONG_2ADDR:
938             case SUB_LONG_2ADDR:
939             case MUL_LONG_2ADDR:
940             case DIV_LONG_2ADDR:
941             case REM_LONG_2ADDR:
942             case AND_LONG_2ADDR:
943             case OR_LONG_2ADDR:
944             case XOR_LONG_2ADDR:
945             case SHL_LONG_2ADDR:
946             case SHR_LONG_2ADDR:
947             case USHR_LONG_2ADDR:
948                 analyzeBinary2AddrOp(analyzedInstruction, RegisterType.LONG_LO_TYPE, false);
949                 return true;
950             case ADD_FLOAT_2ADDR:
951             case SUB_FLOAT_2ADDR:
952             case MUL_FLOAT_2ADDR:
953             case DIV_FLOAT_2ADDR:
954             case REM_FLOAT_2ADDR:
955                 analyzeBinary2AddrOp(analyzedInstruction, RegisterType.FLOAT_TYPE, false);
956                 return true;
957             case ADD_DOUBLE_2ADDR:
958             case SUB_DOUBLE_2ADDR:
959             case MUL_DOUBLE_2ADDR:
960             case DIV_DOUBLE_2ADDR:
961             case REM_DOUBLE_2ADDR:
962                 analyzeBinary2AddrOp(analyzedInstruction, RegisterType.DOUBLE_LO_TYPE, false);
963                 return true;
964             case ADD_INT_LIT16:
965             case RSUB_INT:
966             case MUL_INT_LIT16:
967             case DIV_INT_LIT16:
968             case REM_INT_LIT16:
969                 analyzeLiteralBinaryOp(analyzedInstruction, RegisterType.INTEGER_TYPE, false);
970                 return true;
971             case AND_INT_LIT16:
972             case OR_INT_LIT16:
973             case XOR_INT_LIT16:
974                 analyzeLiteralBinaryOp(analyzedInstruction, RegisterType.INTEGER_TYPE, true);
975                 return true;
976             case ADD_INT_LIT8:
977             case RSUB_INT_LIT8:
978             case MUL_INT_LIT8:
979             case DIV_INT_LIT8:
980             case REM_INT_LIT8:
981             case SHL_INT_LIT8:
982                 analyzeLiteralBinaryOp(analyzedInstruction, RegisterType.INTEGER_TYPE, false);
983                 return true;
984             case AND_INT_LIT8:
985             case OR_INT_LIT8:
986             case XOR_INT_LIT8:
987                 analyzeLiteralBinaryOp(analyzedInstruction, RegisterType.INTEGER_TYPE, true);
988                 return true;
989             case SHR_INT_LIT8:
990                 analyzeLiteralBinaryOp(analyzedInstruction, getDestTypeForLiteralShiftRight(analyzedInstruction, true),
991                         false);
992                 return true;
993             case USHR_INT_LIT8:
994                 analyzeLiteralBinaryOp(analyzedInstruction, getDestTypeForLiteralShiftRight(analyzedInstruction, false),
995                         false);
996                 return true;
997 
998             /*odexed instructions*/
999             case IGET_VOLATILE:
1000             case IPUT_VOLATILE:
1001             case SGET_VOLATILE:
1002             case SPUT_VOLATILE:
1003             case IGET_OBJECT_VOLATILE:
1004             case IGET_WIDE_VOLATILE:
1005             case IPUT_WIDE_VOLATILE:
1006             case SGET_WIDE_VOLATILE:
1007             case SPUT_WIDE_VOLATILE:
1008                 analyzePutGetVolatile(analyzedInstruction);
1009                 return true;
1010             case THROW_VERIFICATION_ERROR:
1011                 return true;
1012             case EXECUTE_INLINE:
1013                 analyzeExecuteInline(analyzedInstruction);
1014                 return true;
1015             case EXECUTE_INLINE_RANGE:
1016                 analyzeExecuteInlineRange(analyzedInstruction);
1017                 return true;
1018             case INVOKE_DIRECT_EMPTY:
1019                 analyzeInvokeDirectEmpty(analyzedInstruction);
1020                 return true;
1021             case INVOKE_OBJECT_INIT_RANGE:
1022                 analyzeInvokeObjectInitRange(analyzedInstruction);
1023                 return true;
1024             case IGET_QUICK:
1025             case IGET_WIDE_QUICK:
1026             case IGET_OBJECT_QUICK:
1027             case IPUT_QUICK:
1028             case IPUT_WIDE_QUICK:
1029             case IPUT_OBJECT_QUICK:
1030             case IPUT_BOOLEAN_QUICK:
1031             case IPUT_BYTE_QUICK:
1032             case IPUT_CHAR_QUICK:
1033             case IPUT_SHORT_QUICK:
1034             case IGET_BOOLEAN_QUICK:
1035             case IGET_BYTE_QUICK:
1036             case IGET_CHAR_QUICK:
1037             case IGET_SHORT_QUICK:
1038                 return analyzeIputIgetQuick(analyzedInstruction);
1039             case INVOKE_VIRTUAL_QUICK:
1040                 return analyzeInvokeVirtualQuick(analyzedInstruction, false, false);
1041             case INVOKE_SUPER_QUICK:
1042                 return analyzeInvokeVirtualQuick(analyzedInstruction, true, false);
1043             case INVOKE_VIRTUAL_QUICK_RANGE:
1044                 return analyzeInvokeVirtualQuick(analyzedInstruction, false, true);
1045             case INVOKE_SUPER_QUICK_RANGE:
1046                 return analyzeInvokeVirtualQuick(analyzedInstruction, true, true);
1047             case IPUT_OBJECT_VOLATILE:
1048             case SGET_OBJECT_VOLATILE:
1049             case SPUT_OBJECT_VOLATILE:
1050                 analyzePutGetVolatile(analyzedInstruction);
1051                 return true;
1052             default:
1053                 assert false;
1054                 return true;
1055         }
1056     }
1057 
1058     private static final BitSet Primitive32BitCategories = BitSetUtils.bitSetOfIndexes(
1059             RegisterType.NULL,
1060             RegisterType.ONE,
1061             RegisterType.BOOLEAN,
1062             RegisterType.BYTE,
1063             RegisterType.POS_BYTE,
1064             RegisterType.SHORT,
1065             RegisterType.POS_SHORT,
1066             RegisterType.CHAR,
1067             RegisterType.INTEGER,
1068             RegisterType.FLOAT);
1069 
1070     private static final BitSet WideLowCategories = BitSetUtils.bitSetOfIndexes(
1071             RegisterType.LONG_LO,
1072             RegisterType.DOUBLE_LO);
1073 
1074     private static final BitSet WideHighCategories = BitSetUtils.bitSetOfIndexes(
1075             RegisterType.LONG_HI,
1076             RegisterType.DOUBLE_HI);
1077 
1078     private static final BitSet ReferenceOrUninitCategories = BitSetUtils.bitSetOfIndexes(
1079             RegisterType.NULL,
1080             RegisterType.UNINIT_REF,
1081             RegisterType.UNINIT_THIS,
1082             RegisterType.REFERENCE);
1083 
1084     private static final BitSet BooleanCategories = BitSetUtils.bitSetOfIndexes(
1085             RegisterType.NULL,
1086             RegisterType.ONE,
1087             RegisterType.BOOLEAN);
1088 
1089     private void analyzeMove(@Nonnull AnalyzedInstruction analyzedInstruction) {
1090         TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
1091 
1092         RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1093         setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, sourceRegisterType);
1094     }
1095 
1096     private void analyzeMoveResult(@Nonnull AnalyzedInstruction analyzedInstruction) {
1097         AnalyzedInstruction previousInstruction = null;
1098         if (analyzedInstruction.instructionIndex > 0) {
1099             previousInstruction = analyzedInstructions.valueAt(analyzedInstruction.instructionIndex-1);
1100         }
1101         if (previousInstruction == null || !previousInstruction.instruction.getOpcode().setsResult()) {
1102             throw new AnalysisException(analyzedInstruction.instruction.getOpcode().name + " must occur after an " +
1103                     "invoke-*/fill-new-array instruction");
1104         }
1105 
1106         RegisterType resultRegisterType;
1107         ReferenceInstruction invokeInstruction = (ReferenceInstruction)previousInstruction.instruction;
1108         Reference reference = invokeInstruction.getReference();
1109 
1110         if (reference instanceof MethodReference) {
1111             resultRegisterType = RegisterType.getRegisterType(classPath, ((MethodReference)reference).getReturnType());
1112         } else {
1113             resultRegisterType = RegisterType.getRegisterType(classPath, (TypeReference)reference);
1114         }
1115 
1116         setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, resultRegisterType);
1117     }
1118 
1119     private void analyzeMoveException(@Nonnull AnalyzedInstruction analyzedInstruction) {
1120         int instructionAddress = getInstructionAddress(analyzedInstruction);
1121 
1122         RegisterType exceptionType = RegisterType.UNKNOWN_TYPE;
1123 
1124         for (TryBlock<? extends ExceptionHandler> tryBlock: methodImpl.getTryBlocks()) {
1125             for (ExceptionHandler handler: tryBlock.getExceptionHandlers()) {
1126 
1127                 if (handler.getHandlerCodeAddress() == instructionAddress) {
1128                     String type = handler.getExceptionType();
1129                     if (type == null) {
1130                         exceptionType = RegisterType.getRegisterType(RegisterType.REFERENCE,
1131                                 classPath.getClass("Ljava/lang/Throwable;"));
1132                     } else {
1133                         exceptionType = RegisterType.getRegisterType(RegisterType.REFERENCE, classPath.getClass(type))
1134                                 .merge(exceptionType);
1135                     }
1136                 }
1137             }
1138         }
1139 
1140         if (exceptionType.category == RegisterType.UNKNOWN) {
1141             throw new AnalysisException("move-exception must be the first instruction in an exception handler block");
1142         }
1143 
1144         setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, exceptionType);
1145     }
1146 
1147     private void analyzeOdexReturnVoid(AnalyzedInstruction analyzedInstruction) {
1148         analyzeOdexReturnVoid(analyzedInstruction, true);
1149     }
1150 
1151     private void analyzeOdexReturnVoid(@Nonnull AnalyzedInstruction analyzedInstruction, boolean analyzeResult) {
1152         Instruction10x deodexedInstruction = new ImmutableInstruction10x(Opcode.RETURN_VOID);
1153 
1154         analyzedInstruction.setDeodexedInstruction(deodexedInstruction);
1155 
1156         if (analyzeResult) {
1157             analyzeInstruction(analyzedInstruction);
1158         }
1159     }
1160 
1161     private void analyzeConst(@Nonnull AnalyzedInstruction analyzedInstruction) {
1162         NarrowLiteralInstruction instruction = (NarrowLiteralInstruction)analyzedInstruction.instruction;
1163 
1164         //we assume that the literal value is a valid value for the given instruction type, because it's impossible
1165         //to store an invalid literal with the instruction. so we don't need to check the type of the literal
1166         setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1167                 RegisterType.getRegisterTypeForLiteral(instruction.getNarrowLiteral()));
1168     }
1169 
1170     private void analyzeWideConst(@Nonnull AnalyzedInstruction analyzedInstruction) {
1171         setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, RegisterType.LONG_LO_TYPE);
1172     }
1173 
1174     private void analyzeConstString(@Nonnull AnalyzedInstruction analyzedInstruction) {
1175         TypeProto stringClass = classPath.getClass("Ljava/lang/String;");
1176         RegisterType stringType = RegisterType.getRegisterType(RegisterType.REFERENCE, stringClass);
1177         setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, stringType);
1178     }
1179 
1180     private void analyzeConstClass(@Nonnull AnalyzedInstruction analyzedInstruction) {
1181         TypeProto classClass = classPath.getClass("Ljava/lang/Class;");
1182         RegisterType classType = RegisterType.getRegisterType(RegisterType.REFERENCE, classClass);
1183         setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, classType);
1184     }
1185 
1186     private void analyzeCheckCast(@Nonnull AnalyzedInstruction analyzedInstruction) {
1187         ReferenceInstruction instruction = (ReferenceInstruction)analyzedInstruction.instruction;
1188         TypeReference reference = (TypeReference)instruction.getReference();
1189         RegisterType castRegisterType = RegisterType.getRegisterType(classPath, reference);
1190         setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, castRegisterType);
1191     }
1192 
1193     public static boolean isNotWideningConversion(RegisterType originalType, RegisterType newType) {
1194         if (originalType.type == null || newType.type == null) {
1195             return true;
1196         }
1197         if (originalType.type.isInterface()) {
1198             return newType.type.implementsInterface(originalType.type.getType());
1199         } else {
1200             TypeProto commonSuperclass = newType.type.getCommonSuperclass(originalType.type);
1201             if (commonSuperclass.getType().equals(originalType.type.getType())) {
1202                 return true;
1203         }
1204             if (commonSuperclass.getType().equals(newType.type.getType())) {
1205                 return false;
1206             }
1207         }
1208         return true;
1209     }
1210 
1211     static boolean canPropagateTypeAfterInstanceOf(AnalyzedInstruction analyzedInstanceOfInstruction,
1212                                                    AnalyzedInstruction analyzedIfInstruction, ClassPath classPath) {
1213         if (!classPath.isArt()) {
1214             return false;
1215         }
1216 
1217         Instruction ifInstruction = analyzedIfInstruction.instruction;
1218         if (((Instruction21t)ifInstruction).getRegisterA() == analyzedInstanceOfInstruction.getDestinationRegister()) {
1219             Reference reference = ((Instruction22c)analyzedInstanceOfInstruction.getInstruction()).getReference();
1220             RegisterType registerType = RegisterType.getRegisterType(classPath, (TypeReference)reference);
1221 
1222             try {
1223                 if (registerType.type != null && !registerType.type.isInterface()) {
1224                     int objectRegister = ((TwoRegisterInstruction)analyzedInstanceOfInstruction.getInstruction())
1225                             .getRegisterB();
1226 
1227                     RegisterType originalType = analyzedIfInstruction.getPreInstructionRegisterType(objectRegister);
1228 
1229                     return isNotWideningConversion(originalType, registerType);
1230                 }
1231             } catch (UnresolvedClassException ex) {
1232                 return false;
1233             }
1234         }
1235         return false;
1236     }
1237 
1238     /**
1239      * Art uses a peephole optimization for an if-eqz or if-nez that occur immediately after an instance-of. It will
1240      * narrow the type if possible, and then NOP out any corresponding check-cast instruction later on
1241      */
1242     private void analyzeIfEqzNez(@Nonnull AnalyzedInstruction analyzedInstruction) {
1243         if (classPath.isArt()) {
1244             int instructionIndex = analyzedInstruction.getInstructionIndex();
1245             if (instructionIndex > 0) {
1246                 if (analyzedInstruction.getPredecessorCount() != 1) {
1247                     return;
1248                 }
1249                 AnalyzedInstruction prevAnalyzedInstruction = analyzedInstruction.getPredecessors().first();
1250                 if (prevAnalyzedInstruction.instruction.getOpcode() == Opcode.INSTANCE_OF) {
1251 
1252                     AnalyzedInstruction fallthroughInstruction = analyzedInstructions.valueAt(
1253                             analyzedInstruction.getInstructionIndex() + 1);
1254 
1255                     int nextAddress = getInstructionAddress(analyzedInstruction) +
1256                             ((Instruction21t)analyzedInstruction.instruction).getCodeOffset();
1257                     AnalyzedInstruction branchInstruction = analyzedInstructions.get(nextAddress);
1258 
1259                     int narrowingRegister = ((Instruction22c)prevAnalyzedInstruction.instruction).getRegisterB();
1260                     RegisterType originalType = analyzedInstruction.getPreInstructionRegisterType(narrowingRegister);
1261 
1262                     Instruction22c instanceOfInstruction = (Instruction22c)prevAnalyzedInstruction.instruction;
1263                     RegisterType newType = RegisterType.getRegisterType(classPath,
1264                             (TypeReference)instanceOfInstruction.getReference());
1265 
1266                     for (int register : analyzedInstruction.getSetRegisters()) {
1267                         if (analyzedInstruction.instruction.getOpcode() == Opcode.IF_EQZ) {
1268                             overridePredecessorRegisterTypeAndPropagateChanges(fallthroughInstruction,
1269                                     analyzedInstruction, register, newType);
1270                             overridePredecessorRegisterTypeAndPropagateChanges(branchInstruction, analyzedInstruction,
1271                                     register, originalType);
1272                         } else {
1273                             overridePredecessorRegisterTypeAndPropagateChanges(fallthroughInstruction,
1274                                     analyzedInstruction, register, originalType);
1275                             overridePredecessorRegisterTypeAndPropagateChanges(branchInstruction, analyzedInstruction,
1276                                     register, newType);
1277                         }
1278                     }
1279                 }
1280             }
1281         }
1282     }
1283 
1284     private void analyzeInstanceOf(@Nonnull AnalyzedInstruction analyzedInstruction) {
1285         setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, RegisterType.BOOLEAN_TYPE);
1286     }
1287 
1288     private void analyzeArrayLength(@Nonnull AnalyzedInstruction analyzedInstruction) {
1289         setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, RegisterType.INTEGER_TYPE);
1290     }
1291 
1292     private void analyzeNewInstance(@Nonnull AnalyzedInstruction analyzedInstruction) {
1293         ReferenceInstruction instruction = (ReferenceInstruction)analyzedInstruction.instruction;
1294 
1295         int register = ((OneRegisterInstruction)analyzedInstruction.instruction).getRegisterA();
1296         RegisterType destRegisterType = analyzedInstruction.getPostInstructionRegisterType(register);
1297         if (destRegisterType.category != RegisterType.UNKNOWN) {
1298             //the post-instruction destination register will only be set if we have already analyzed this instruction
1299             //at least once. If this is the case, then the uninit reference has already been propagated to all
1300             //successors and nothing else needs to be done.
1301             assert destRegisterType.category == RegisterType.UNINIT_REF;
1302             return;
1303         }
1304 
1305         TypeReference typeReference = (TypeReference)instruction.getReference();
1306 
1307         RegisterType classType = RegisterType.getRegisterType(classPath, typeReference);
1308 
1309         setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1310                 RegisterType.getRegisterType(RegisterType.UNINIT_REF, classType.type));
1311     }
1312 
1313     private void analyzeNewArray(@Nonnull AnalyzedInstruction analyzedInstruction) {
1314         ReferenceInstruction instruction = (ReferenceInstruction)analyzedInstruction.instruction;
1315 
1316         TypeReference type = (TypeReference)instruction.getReference();
1317         if (type.getType().charAt(0) != '[') {
1318             throw new AnalysisException("new-array used with non-array type");
1319         }
1320 
1321         RegisterType arrayType = RegisterType.getRegisterType(classPath, type);
1322 
1323         setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, arrayType);
1324     }
1325 
1326     private void analyzeFloatWideCmp(@Nonnull AnalyzedInstruction analyzedInstruction) {
1327         setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, RegisterType.BYTE_TYPE);
1328     }
1329 
1330     private void analyze32BitPrimitiveAget(@Nonnull AnalyzedInstruction analyzedInstruction,
1331                                            @Nonnull RegisterType registerType) {
1332         setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, registerType);
1333     }
1334 
1335     private void analyzeAgetWide(@Nonnull AnalyzedInstruction analyzedInstruction) {
1336         ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
1337 
1338         RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1339         if (arrayRegisterType.category != RegisterType.NULL) {
1340             if (arrayRegisterType.category != RegisterType.REFERENCE ||
1341                     !(arrayRegisterType.type instanceof ArrayProto)) {
1342                 throw new AnalysisException("aget-wide used with non-array register: %s", arrayRegisterType.toString());
1343             }
1344             ArrayProto arrayProto = (ArrayProto)arrayRegisterType.type;
1345 
1346             if (arrayProto.dimensions != 1) {
1347                 throw new AnalysisException("aget-wide used with multi-dimensional array: %s",
1348                         arrayRegisterType.toString());
1349             }
1350 
1351             char arrayBaseType = arrayProto.getElementType().charAt(0);
1352             if (arrayBaseType == 'J') {
1353                 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, RegisterType.LONG_LO_TYPE);
1354             } else if (arrayBaseType == 'D') {
1355                 setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, RegisterType.DOUBLE_LO_TYPE);
1356             } else {
1357                 throw new AnalysisException("aget-wide used with narrow array: %s", arrayRegisterType);
1358             }
1359         } else {
1360             // If the array register is null, we can assume that the destination register was meant to be a wide type.
1361             // This is the same behavior as dalvik's verifier
1362             setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, RegisterType.LONG_LO_TYPE);
1363         }
1364     }
1365 
1366     private void analyzeAgetObject(@Nonnull AnalyzedInstruction analyzedInstruction) {
1367         ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
1368 
1369         RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1370         if (arrayRegisterType.category != RegisterType.NULL) {
1371             if (arrayRegisterType.category != RegisterType.REFERENCE ||
1372                     !(arrayRegisterType.type instanceof ArrayProto)) {
1373                 throw new AnalysisException("aget-object used with non-array register: %s",
1374                         arrayRegisterType.toString());
1375             }
1376 
1377             ArrayProto arrayProto = (ArrayProto)arrayRegisterType.type;
1378 
1379             String elementType = arrayProto.getImmediateElementType();
1380 
1381             setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1382                     RegisterType.getRegisterType(RegisterType.REFERENCE, classPath.getClass(elementType)));
1383         } else {
1384             // If the array register is null, we can assume that the destination register was meant to be a reference
1385             // type, so we set the destination to NULL. This is the same behavior as dalvik's verifier
1386             setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, RegisterType.NULL_TYPE);
1387         }
1388     }
1389 
1390     private void analyze32BitPrimitiveIgetSget(@Nonnull AnalyzedInstruction analyzedInstruction,
1391                                                @Nonnull RegisterType registerType) {
1392         setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, registerType);
1393     }
1394 
1395     private void analyzeIgetSgetWideObject(@Nonnull AnalyzedInstruction analyzedInstruction) {
1396         ReferenceInstruction referenceInstruction = (ReferenceInstruction)analyzedInstruction.instruction;
1397 
1398         FieldReference fieldReference = (FieldReference)referenceInstruction.getReference();
1399 
1400         RegisterType fieldType = RegisterType.getRegisterType(classPath, fieldReference.getType());
1401         setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, fieldType);
1402     }
1403 
1404     private void analyzeInvokeDirect(@Nonnull AnalyzedInstruction analyzedInstruction) {
1405         FiveRegisterInstruction instruction = (FiveRegisterInstruction)analyzedInstruction.instruction;
1406         analyzeInvokeDirectCommon(analyzedInstruction, instruction.getRegisterC());
1407     }
1408 
1409     private void analyzeInvokeDirectRange(@Nonnull AnalyzedInstruction analyzedInstruction) {
1410         RegisterRangeInstruction instruction = (RegisterRangeInstruction)analyzedInstruction.instruction;
1411         analyzeInvokeDirectCommon(analyzedInstruction, instruction.getStartRegister());
1412     }
1413 
1414     private void analyzeInvokeDirectCommon(@Nonnull AnalyzedInstruction analyzedInstruction, int objectRegister) {
1415         // This handles the case of invoking a constructor on an uninitialized reference. This propagates the
1416         // initialized type for the object register, and also any known aliased registers.
1417         //
1418         // In some cases, unrelated uninitialized references may not have been propagated past this instruction. This
1419         // happens when propagating those types and the type of object register of this instruction isn't known yet.
1420         // In this case, we can't determine if the uninitialized reference being propagated in an alias of the object
1421         // register, so we don't stop propagation.
1422         //
1423         // We check for any of these unpropagated uninitialized references here and propagate them.
1424         if (analyzedInstruction.isInvokeInit()) {
1425             RegisterType uninitRef = analyzedInstruction.getPreInstructionRegisterType(objectRegister);
1426             if (uninitRef.category != RegisterType.UNINIT_REF && uninitRef.category != RegisterType.UNINIT_THIS) {
1427                 assert analyzedInstruction.getSetRegisters().isEmpty();
1428                 return;
1429             }
1430 
1431             RegisterType initRef = RegisterType.getRegisterType(RegisterType.REFERENCE, uninitRef.type);
1432 
1433             for (int register: analyzedInstruction.getSetRegisters()) {
1434                 RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(register);
1435 
1436                 if (registerType == uninitRef) {
1437                     setPostRegisterTypeAndPropagateChanges(analyzedInstruction, register, initRef);
1438                 } else {
1439                     // This is unrelated uninitialized reference. propagate it as-is
1440                     setPostRegisterTypeAndPropagateChanges(analyzedInstruction, register, registerType);
1441                 }
1442             }
1443         }
1444     }
1445 
1446     private void analyzeUnaryOp(@Nonnull AnalyzedInstruction analyzedInstruction,
1447                                 @Nonnull RegisterType destRegisterType) {
1448         setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, destRegisterType);
1449     }
1450 
1451     private void analyzeBinaryOp(@Nonnull AnalyzedInstruction analyzedInstruction,
1452                                  @Nonnull RegisterType destRegisterType, boolean checkForBoolean) {
1453         if (checkForBoolean) {
1454             ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
1455 
1456             RegisterType source1RegisterType =
1457                     analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1458             RegisterType source2RegisterType =
1459                     analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterC());
1460 
1461             if (BooleanCategories.get(source1RegisterType.category) &&
1462                     BooleanCategories.get(source2RegisterType.category)) {
1463                 destRegisterType = RegisterType.BOOLEAN_TYPE;
1464             }
1465         }
1466 
1467         setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, destRegisterType);
1468     }
1469 
1470     private void analyzeBinary2AddrOp(@Nonnull AnalyzedInstruction analyzedInstruction,
1471                                       @Nonnull RegisterType destRegisterType, boolean checkForBoolean) {
1472         if (checkForBoolean) {
1473             TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
1474 
1475             RegisterType source1RegisterType =
1476                     analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA());
1477             RegisterType source2RegisterType =
1478                     analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1479 
1480             if (BooleanCategories.get(source1RegisterType.category) &&
1481                     BooleanCategories.get(source2RegisterType.category)) {
1482                 destRegisterType = RegisterType.BOOLEAN_TYPE;
1483             }
1484         }
1485 
1486         setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, destRegisterType);
1487     }
1488 
1489     private void analyzeLiteralBinaryOp(@Nonnull AnalyzedInstruction analyzedInstruction,
1490                                         @Nonnull RegisterType destRegisterType, boolean checkForBoolean) {
1491         if (checkForBoolean) {
1492             TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
1493 
1494             RegisterType sourceRegisterType =
1495                     analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1496 
1497             if (BooleanCategories.get(sourceRegisterType.category)) {
1498                 int literal = ((NarrowLiteralInstruction)analyzedInstruction.instruction).getNarrowLiteral();
1499                 if (literal == 0 || literal == 1) {
1500                     destRegisterType = RegisterType.BOOLEAN_TYPE;
1501                 }
1502             }
1503         }
1504 
1505         setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, destRegisterType);
1506     }
1507 
1508     private RegisterType getDestTypeForLiteralShiftRight(@Nonnull AnalyzedInstruction analyzedInstruction, boolean signedShift) {
1509         TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
1510 
1511         RegisterType sourceRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
1512                 Primitive32BitCategories);
1513         long literalShift = ((NarrowLiteralInstruction)analyzedInstruction.instruction).getNarrowLiteral();
1514 
1515         if (literalShift == 0) {
1516             return sourceRegisterType;
1517         }
1518 
1519         RegisterType destRegisterType;
1520         if (!signedShift) {
1521             destRegisterType = RegisterType.INTEGER_TYPE;
1522         } else {
1523             destRegisterType = sourceRegisterType;
1524         }
1525 
1526         literalShift = literalShift & 0x1f;
1527 
1528         switch (sourceRegisterType.category) {
1529             case RegisterType.INTEGER:
1530             case RegisterType.FLOAT:
1531                 if (!signedShift) {
1532                     if (literalShift > 24) {
1533                         return RegisterType.POS_BYTE_TYPE;
1534                     }
1535                     if (literalShift >= 16) {
1536                         return RegisterType.CHAR_TYPE;
1537                     }
1538                 } else {
1539                     if (literalShift >= 24) {
1540                         return RegisterType.BYTE_TYPE;
1541                     }
1542                     if (literalShift >= 16) {
1543                         return RegisterType.SHORT_TYPE;
1544                     }
1545                 }
1546                 break;
1547             case RegisterType.SHORT:
1548                 if (signedShift && literalShift >= 8) {
1549                     return RegisterType.BYTE_TYPE;
1550                 }
1551                 break;
1552             case RegisterType.POS_SHORT:
1553                 if (literalShift >= 8) {
1554                     return RegisterType.POS_BYTE_TYPE;
1555                 }
1556                 break;
1557             case RegisterType.CHAR:
1558                 if (literalShift > 8) {
1559                     return RegisterType.POS_BYTE_TYPE;
1560                 }
1561                 break;
1562             case RegisterType.BYTE:
1563                 break;
1564             case RegisterType.POS_BYTE:
1565                 return RegisterType.POS_BYTE_TYPE;
1566             case RegisterType.NULL:
1567             case RegisterType.ONE:
1568             case RegisterType.BOOLEAN:
1569                 return RegisterType.NULL_TYPE;
1570             default:
1571                 assert false;
1572         }
1573 
1574         return destRegisterType;
1575     }
1576 
1577 
1578     private void analyzeExecuteInline(@Nonnull AnalyzedInstruction analyzedInstruction) {
1579         if (inlineResolver == null) {
1580             throw new AnalysisException("Cannot analyze an odexed instruction unless we are deodexing");
1581         }
1582 
1583         Instruction35mi instruction = (Instruction35mi)analyzedInstruction.instruction;
1584         Method resolvedMethod = inlineResolver.resolveExecuteInline(analyzedInstruction);
1585 
1586         Opcode deodexedOpcode;
1587         int acccessFlags = resolvedMethod.getAccessFlags();
1588         if (AccessFlags.STATIC.isSet(acccessFlags)) {
1589             deodexedOpcode = Opcode.INVOKE_STATIC;
1590         } else if (AccessFlags.PRIVATE.isSet(acccessFlags)) {
1591             deodexedOpcode = Opcode.INVOKE_DIRECT;
1592         } else {
1593             deodexedOpcode = Opcode.INVOKE_VIRTUAL;
1594         }
1595 
1596         Instruction35c deodexedInstruction = new ImmutableInstruction35c(deodexedOpcode, instruction.getRegisterCount(),
1597                 instruction.getRegisterC(), instruction.getRegisterD(), instruction.getRegisterE(),
1598                 instruction.getRegisterF(), instruction.getRegisterG(), resolvedMethod);
1599 
1600         analyzedInstruction.setDeodexedInstruction(deodexedInstruction);
1601         analyzeInstruction(analyzedInstruction);
1602     }
1603 
1604     private void analyzeExecuteInlineRange(@Nonnull AnalyzedInstruction analyzedInstruction) {
1605         if (inlineResolver == null) {
1606             throw new AnalysisException("Cannot analyze an odexed instruction unless we are deodexing");
1607         }
1608 
1609         Instruction3rmi instruction = (Instruction3rmi)analyzedInstruction.instruction;
1610         Method resolvedMethod = inlineResolver.resolveExecuteInline(analyzedInstruction);
1611 
1612         Opcode deodexedOpcode;
1613         int acccessFlags = resolvedMethod.getAccessFlags();
1614         if (AccessFlags.STATIC.isSet(acccessFlags)) {
1615             deodexedOpcode = Opcode.INVOKE_STATIC_RANGE;
1616         } else if (AccessFlags.PRIVATE.isSet(acccessFlags)) {
1617             deodexedOpcode = Opcode.INVOKE_DIRECT_RANGE;
1618         } else {
1619             deodexedOpcode = Opcode.INVOKE_VIRTUAL_RANGE;
1620         }
1621 
1622         Instruction3rc deodexedInstruction = new ImmutableInstruction3rc(deodexedOpcode, instruction.getStartRegister(),
1623                 instruction.getRegisterCount(), resolvedMethod);
1624 
1625         analyzedInstruction.setDeodexedInstruction(deodexedInstruction);
1626         analyzeInstruction(analyzedInstruction);
1627     }
1628 
1629     private void analyzeInvokeDirectEmpty(@Nonnull AnalyzedInstruction analyzedInstruction) {
1630         analyzeInvokeDirectEmpty(analyzedInstruction, true);
1631     }
1632 
1633     private void analyzeInvokeDirectEmpty(@Nonnull AnalyzedInstruction analyzedInstruction, boolean analyzeResult) {
1634         Instruction35c instruction = (Instruction35c)analyzedInstruction.instruction;
1635 
1636         Instruction35c deodexedInstruction = new ImmutableInstruction35c(Opcode.INVOKE_DIRECT,
1637                 instruction.getRegisterCount(), instruction.getRegisterC(), instruction.getRegisterD(),
1638                 instruction.getRegisterE(), instruction.getRegisterF(), instruction.getRegisterG(),
1639                 instruction.getReference());
1640 
1641         analyzedInstruction.setDeodexedInstruction(deodexedInstruction);
1642 
1643         if (analyzeResult) {
1644             analyzeInstruction(analyzedInstruction);
1645         }
1646     }
1647 
1648     private void analyzeInvokeObjectInitRange(@Nonnull AnalyzedInstruction analyzedInstruction) {
1649         analyzeInvokeObjectInitRange(analyzedInstruction, true);
1650     }
1651 
1652     private void analyzeInvokeObjectInitRange(@Nonnull AnalyzedInstruction analyzedInstruction, boolean analyzeResult) {
1653         Instruction3rc instruction = (Instruction3rc)analyzedInstruction.instruction;
1654 
1655         Instruction deodexedInstruction;
1656 
1657         int startRegister = instruction.getStartRegister();
1658         // hack: we should be using instruction.getRegisterCount, but some tweaked versions of dalvik appear
1659         // to generate invoke-object-init/range instructions with an invalid register count. We know it should
1660         // always be 1, so just use that.
1661         int registerCount = 1;
1662         if (startRegister < 16) {
1663             deodexedInstruction = new ImmutableInstruction35c(Opcode.INVOKE_DIRECT,
1664                     registerCount, startRegister, 0, 0, 0, 0, instruction.getReference());
1665         } else {
1666             deodexedInstruction = new ImmutableInstruction3rc(Opcode.INVOKE_DIRECT_RANGE,
1667                     startRegister, registerCount, instruction.getReference());
1668         }
1669 
1670         analyzedInstruction.setDeodexedInstruction(deodexedInstruction);
1671 
1672         if (analyzeResult) {
1673             analyzeInstruction(analyzedInstruction);
1674         }
1675     }
1676 
1677     private boolean analyzeIputIgetQuick(@Nonnull AnalyzedInstruction analyzedInstruction) {
1678         Instruction22cs instruction = (Instruction22cs)analyzedInstruction.instruction;
1679 
1680         int fieldOffset = instruction.getFieldOffset();
1681         RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
1682                 ReferenceOrUninitCategories);
1683 
1684         if (objectRegisterType.category == RegisterType.NULL) {
1685             return false;
1686         }
1687 
1688         TypeProto objectRegisterTypeProto = objectRegisterType.type;
1689         assert objectRegisterTypeProto != null;
1690 
1691         TypeProto classTypeProto = classPath.getClass(objectRegisterTypeProto.getType());
1692         FieldReference resolvedField = classTypeProto.getFieldByOffset(fieldOffset);
1693 
1694         if (resolvedField == null) {
1695             throw new AnalysisException("Could not resolve the field in class %s at offset %d",
1696                     objectRegisterType.type.getType(), fieldOffset);
1697         }
1698 
1699         ClassDef thisClass = classPath.getClassDef(method.getDefiningClass());
1700 
1701         if (!TypeUtils.canAccessClass(thisClass.getType(), classPath.getClassDef(resolvedField.getDefiningClass()))) {
1702 
1703             // the class is not accessible. So we start looking at objectRegisterTypeProto (which may be different
1704             // than resolvedField.getDefiningClass()), and walk up the class hierarchy.
1705             ClassDef fieldClass = classPath.getClassDef(objectRegisterTypeProto.getType());
1706             while (!TypeUtils.canAccessClass(thisClass.getType(), fieldClass)) {
1707                 String superclass = fieldClass.getSuperclass();
1708                 if (superclass == null) {
1709                     throw new ExceptionWithContext("Couldn't find accessible class while resolving field %s",
1710                             ReferenceUtil.getShortFieldDescriptor(resolvedField));
1711                 }
1712 
1713                 fieldClass = classPath.getClassDef(superclass);
1714             }
1715 
1716             // fieldClass is now the first accessible class found. Now. we need to make sure that the field is
1717             // actually valid for this class
1718             FieldReference newResolvedField = classPath.getClass(fieldClass.getType()).getFieldByOffset(fieldOffset);
1719             if (newResolvedField == null) {
1720                 throw new ExceptionWithContext("Couldn't find accessible class while resolving field %s",
1721                         ReferenceUtil.getShortFieldDescriptor(resolvedField));
1722             }
1723             resolvedField = new ImmutableFieldReference(fieldClass.getType(), newResolvedField.getName(),
1724                     newResolvedField.getType());
1725         }
1726 
1727         String fieldType = resolvedField.getType();
1728 
1729         Opcode opcode = classPath.getFieldInstructionMapper().getAndCheckDeodexedOpcode(
1730                 fieldType, instruction.getOpcode());
1731 
1732         Instruction22c deodexedInstruction = new ImmutableInstruction22c(opcode, (byte)instruction.getRegisterA(),
1733                 (byte)instruction.getRegisterB(), resolvedField);
1734         analyzedInstruction.setDeodexedInstruction(deodexedInstruction);
1735 
1736         analyzeInstruction(analyzedInstruction);
1737 
1738         return true;
1739     }
1740 
1741     private boolean analyzeInvokeVirtual(@Nonnull AnalyzedInstruction analyzedInstruction, boolean isRange) {
1742         MethodReference targetMethod;
1743 
1744         if (!normalizeVirtualMethods) {
1745             return true;
1746         }
1747 
1748         if (isRange) {
1749             Instruction3rc instruction = (Instruction3rc)analyzedInstruction.instruction;
1750             targetMethod = (MethodReference)instruction.getReference();
1751         } else {
1752             Instruction35c instruction = (Instruction35c)analyzedInstruction.instruction;
1753             targetMethod = (MethodReference)instruction.getReference();
1754         }
1755 
1756         MethodReference replacementMethod = normalizeMethodReference(targetMethod);
1757 
1758         if (replacementMethod == null || replacementMethod.equals(targetMethod)) {
1759             return true;
1760         }
1761 
1762         Instruction deodexedInstruction;
1763         if (isRange) {
1764             Instruction3rc instruction = (Instruction3rc)analyzedInstruction.instruction;
1765             deodexedInstruction = new ImmutableInstruction3rc(instruction.getOpcode(), instruction.getStartRegister(),
1766                     instruction.getRegisterCount(), replacementMethod);
1767         } else {
1768             Instruction35c instruction = (Instruction35c)analyzedInstruction.instruction;
1769             deodexedInstruction = new ImmutableInstruction35c(instruction.getOpcode(), instruction.getRegisterCount(),
1770                     instruction.getRegisterC(), instruction.getRegisterD(), instruction.getRegisterE(),
1771                     instruction.getRegisterF(), instruction.getRegisterG(), replacementMethod);
1772         }
1773 
1774         analyzedInstruction.setDeodexedInstruction(deodexedInstruction);
1775         return true;
1776     }
1777 
1778     private boolean analyzeInvokeVirtualQuick(@Nonnull AnalyzedInstruction analyzedInstruction, boolean isSuper,
1779                                               boolean isRange) {
1780         int methodIndex;
1781         int objectRegister;
1782 
1783         if (isRange) {
1784             Instruction3rms instruction = (Instruction3rms)analyzedInstruction.instruction;
1785             methodIndex = instruction.getVtableIndex();
1786             objectRegister = instruction.getStartRegister();
1787         } else {
1788             Instruction35ms instruction = (Instruction35ms)analyzedInstruction.instruction;
1789             methodIndex = instruction.getVtableIndex();
1790             objectRegister = instruction.getRegisterC();
1791         }
1792 
1793         RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, objectRegister,
1794                 ReferenceOrUninitCategories);
1795         TypeProto objectRegisterTypeProto = objectRegisterType.type;
1796 
1797         if (objectRegisterType.category == RegisterType.NULL) {
1798             return false;
1799         }
1800 
1801         assert objectRegisterTypeProto != null;
1802 
1803         MethodReference resolvedMethod;
1804         if (isSuper) {
1805             // invoke-super is only used for the same class that we're currently in
1806             TypeProto typeProto = classPath.getClass(method.getDefiningClass());
1807             TypeProto superType;
1808 
1809             String superclassType = typeProto.getSuperclass();
1810             if (superclassType != null) {
1811                 superType = classPath.getClass(superclassType);
1812             } else {
1813                 // This is either java.lang.Object, or an UnknownClassProto
1814                 superType = typeProto;
1815             }
1816 
1817             resolvedMethod = superType.getMethodByVtableIndex(methodIndex);
1818         } else {
1819             resolvedMethod = objectRegisterTypeProto.getMethodByVtableIndex(methodIndex);
1820         }
1821 
1822         if (resolvedMethod == null) {
1823             throw new AnalysisException("Could not resolve the method in class %s at index %d",
1824                     objectRegisterType.type.getType(), methodIndex);
1825         }
1826 
1827         // no need to check class access for invoke-super. A class can obviously access its superclass.
1828         ClassDef thisClass = classPath.getClassDef(method.getDefiningClass());
1829 
1830         if (classPath.getClass(resolvedMethod.getDefiningClass()).isInterface()) {
1831             resolvedMethod = new ReparentedMethodReference(resolvedMethod, objectRegisterTypeProto.getType());
1832         } else if (!isSuper && !TypeUtils.canAccessClass(
1833                 thisClass.getType(), classPath.getClassDef(resolvedMethod.getDefiningClass()))) {
1834 
1835             // the class is not accessible. So we start looking at objectRegisterTypeProto (which may be different
1836             // than resolvedMethod.getDefiningClass()), and walk up the class hierarchy.
1837             ClassDef methodClass = classPath.getClassDef(objectRegisterTypeProto.getType());
1838             while (!TypeUtils.canAccessClass(thisClass.getType(), methodClass)) {
1839                 String superclass = methodClass.getSuperclass();
1840                 if (superclass == null) {
1841                     throw new ExceptionWithContext("Couldn't find accessible class while resolving method %s",
1842                             ReferenceUtil.getMethodDescriptor(resolvedMethod, true));
1843                 }
1844 
1845                 methodClass = classPath.getClassDef(superclass);
1846             }
1847 
1848             // methodClass is now the first accessible class found. Now. we need to make sure that the method is
1849             // actually valid for this class
1850             MethodReference newResolvedMethod =
1851                     classPath.getClass(methodClass.getType()).getMethodByVtableIndex(methodIndex);
1852             if (newResolvedMethod == null) {
1853                 throw new ExceptionWithContext("Couldn't find accessible class while resolving method %s",
1854                         ReferenceUtil.getMethodDescriptor(resolvedMethod, true));
1855             }
1856             resolvedMethod = newResolvedMethod;
1857             resolvedMethod = new ImmutableMethodReference(methodClass.getType(), resolvedMethod.getName(),
1858                     resolvedMethod.getParameterTypes(), resolvedMethod.getReturnType());
1859 
1860         }
1861 
1862         if (normalizeVirtualMethods) {
1863             MethodReference replacementMethod = normalizeMethodReference(resolvedMethod);
1864             if (replacementMethod != null) {
1865                 resolvedMethod = replacementMethod;
1866             }
1867         }
1868 
1869         Instruction deodexedInstruction;
1870         if (isRange) {
1871             Instruction3rms instruction = (Instruction3rms)analyzedInstruction.instruction;
1872             Opcode opcode;
1873             if (isSuper) {
1874                 opcode = Opcode.INVOKE_SUPER_RANGE;
1875             } else {
1876                 opcode = Opcode.INVOKE_VIRTUAL_RANGE;
1877             }
1878 
1879             deodexedInstruction = new ImmutableInstruction3rc(opcode, instruction.getStartRegister(),
1880                     instruction.getRegisterCount(), resolvedMethod);
1881         } else {
1882             Instruction35ms instruction = (Instruction35ms)analyzedInstruction.instruction;
1883             Opcode opcode;
1884             if (isSuper) {
1885                 opcode = Opcode.INVOKE_SUPER;
1886             } else {
1887                 opcode = Opcode.INVOKE_VIRTUAL;
1888             }
1889 
1890             deodexedInstruction = new ImmutableInstruction35c(opcode, instruction.getRegisterCount(),
1891                     instruction.getRegisterC(), instruction.getRegisterD(), instruction.getRegisterE(),
1892                     instruction.getRegisterF(), instruction.getRegisterG(), resolvedMethod);
1893         }
1894 
1895         analyzedInstruction.setDeodexedInstruction(deodexedInstruction);
1896         analyzeInstruction(analyzedInstruction);
1897 
1898         return true;
1899     }
1900 
1901     private boolean analyzePutGetVolatile(@Nonnull AnalyzedInstruction analyzedInstruction) {
1902         return analyzePutGetVolatile(analyzedInstruction, true);
1903     }
1904 
1905     private boolean analyzePutGetVolatile(@Nonnull AnalyzedInstruction analyzedInstruction, boolean analyzeResult) {
1906         FieldReference field = (FieldReference)((ReferenceInstruction)analyzedInstruction.instruction).getReference();
1907         String fieldType = field.getType();
1908 
1909         Opcode originalOpcode = analyzedInstruction.instruction.getOpcode();
1910 
1911         Opcode opcode = classPath.getFieldInstructionMapper().getAndCheckDeodexedOpcode(
1912                 fieldType, originalOpcode);
1913 
1914         Instruction deodexedInstruction;
1915 
1916         if (originalOpcode.isStaticFieldAccessor()) {
1917             OneRegisterInstruction instruction = (OneRegisterInstruction)analyzedInstruction.instruction;
1918             deodexedInstruction = new ImmutableInstruction21c(opcode, instruction.getRegisterA(), field);
1919         } else {
1920             TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
1921 
1922             deodexedInstruction = new ImmutableInstruction22c(opcode, instruction.getRegisterA(),
1923                     instruction.getRegisterB(), field);
1924         }
1925 
1926         analyzedInstruction.setDeodexedInstruction(deodexedInstruction);
1927 
1928         if (analyzeResult) {
1929             analyzeInstruction(analyzedInstruction);
1930         }
1931         return true;
1932     }
1933 
1934     @Nonnull
1935     private static RegisterType getAndCheckSourceRegister(@Nonnull AnalyzedInstruction analyzedInstruction,
1936                                                           int registerNumber, BitSet validCategories) {
1937         assert registerNumber >= 0 && registerNumber < analyzedInstruction.postRegisterMap.length;
1938 
1939         RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(registerNumber);
1940 
1941         checkRegister(registerType, registerNumber, validCategories);
1942 
1943         if (validCategories == WideLowCategories) {
1944             checkRegister(registerType, registerNumber, WideLowCategories);
1945             checkWidePair(registerNumber, analyzedInstruction);
1946 
1947             RegisterType secondRegisterType = analyzedInstruction.getPreInstructionRegisterType(registerNumber + 1);
1948             checkRegister(secondRegisterType, registerNumber+1, WideHighCategories);
1949         }
1950 
1951         return registerType;
1952     }
1953 
1954     private static void checkRegister(RegisterType registerType, int registerNumber, BitSet validCategories) {
1955         if (!validCategories.get(registerType.category)) {
1956             throw new AnalysisException(String.format("Invalid register type %s for register v%d.",
1957                     registerType.toString(), registerNumber));
1958         }
1959     }
1960 
1961     private static void checkWidePair(int registerNumber, AnalyzedInstruction analyzedInstruction) {
1962         if (registerNumber + 1 >= analyzedInstruction.postRegisterMap.length) {
1963             throw new AnalysisException(String.format("v%d cannot be used as the first register in a wide register" +
1964                     "pair because it is the last register.", registerNumber));
1965         }
1966     }
1967 
1968     @Nullable
1969     private MethodReference normalizeMethodReference(@Nonnull MethodReference methodRef) {
1970         TypeProto typeProto = classPath.getClass(methodRef.getDefiningClass());
1971         int methodIndex;
1972         try {
1973             methodIndex = typeProto.findMethodIndexInVtable(methodRef);
1974         } catch (UnresolvedClassException ex) {
1975             return null;
1976         }
1977 
1978         if (methodIndex < 0) {
1979             return null;
1980         }
1981 
1982         ClassProto thisClass = (ClassProto)classPath.getClass(method.getDefiningClass());
1983 
1984         Method replacementMethod = typeProto.getMethodByVtableIndex(methodIndex);
1985         assert replacementMethod != null;
1986         while (true) {
1987             String superType = typeProto.getSuperclass();
1988             if (superType == null) {
1989                 break;
1990             }
1991             typeProto = classPath.getClass(superType);
1992             Method resolvedMethod = typeProto.getMethodByVtableIndex(methodIndex);
1993             if (resolvedMethod == null) {
1994                 break;
1995             }
1996 
1997             if (!resolvedMethod.equals(replacementMethod)) {
1998                 if (!AnalyzedMethodUtil.canAccess(thisClass, resolvedMethod, false, false, true)) {
1999                     continue;
2000                 }
2001 
2002                 replacementMethod = resolvedMethod;
2003             }
2004         }
2005         return replacementMethod;
2006     }
2007 
2008     private static class ReparentedMethodReference extends BaseMethodReference {
2009         private final MethodReference baseReference;
2010         private final String definingClass;
2011 
2012         public ReparentedMethodReference(MethodReference baseReference, String definingClass) {
2013             this.baseReference = baseReference;
2014             this.definingClass = definingClass;
2015         }
2016 
2017         @Override @Nonnull public String getName() {
2018             return baseReference.getName();
2019         }
2020 
2021         @Override @Nonnull public List<? extends CharSequence> getParameterTypes() {
2022             return baseReference.getParameterTypes();
2023         }
2024 
2025         @Override @Nonnull public String getReturnType() {
2026             return baseReference.getReturnType();
2027         }
2028 
2029         @Nonnull @Override public String getDefiningClass() {
2030             return definingClass;
2031         }
2032     }
2033 }