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