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