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