MethodAnalyzer.java revision b0c62b9781751e4d04d2ddb4458940d545cc8bb2
1/*
2 * [The "BSD licence"]
3 * Copyright (c) 2010 Ben Gruver (JesusFreke)
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 *    derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29package org.jf.dexlib.Code.Analysis;
30
31import org.jf.dexlib.*;
32import org.jf.dexlib.Code.*;
33import org.jf.dexlib.Code.Format.*;
34import org.jf.dexlib.Util.AccessFlags;
35import org.jf.dexlib.Util.ExceptionWithContext;
36import org.jf.dexlib.Util.SparseArray;
37
38import java.util.BitSet;
39import java.util.EnumSet;
40import java.util.List;
41
42/**
43 * The MethodAnalyzer performs several functions. It "analyzes" the instructions and infers the register types
44 * for each register, it can deodex odexed instructions, and it can verify the bytecode. The analysis and verification
45 * are done in two separate passes, because the analysis has to process instructions multiple times in some cases, and
46 * there's no need to perform the verification multiple times, so we wait until the method is fully analyzed and then
47 * verify it.
48 *
49 * Before calling the analyze() method, you must have initialized the ClassPath by calling
50 * ClassPath.InitializeClassPath
51 */
52public class MethodAnalyzer {
53    private final ClassDataItem.EncodedMethod encodedMethod;
54
55    private final DeodexUtil deodexUtil;
56
57    private SparseArray<AnalyzedInstruction> instructions;
58
59    private static final int NOT_ANALYZED = 0;
60    private static final int ANALYZED = 1;
61    private static final int VERIFIED = 2;
62    private int analyzerState = NOT_ANALYZED;
63
64    private BitSet analyzedInstructions;
65
66    private ValidationException validationException = null;
67
68    //This is a dummy instruction that occurs immediately before the first real instruction. We can initialize the
69    //register types for this instruction to the parameter types, in order to have them propagate to all of its
70    //successors, e.g. the first real instruction, the first instructions in any exception handlers covering the first
71    //instruction, etc.
72    private AnalyzedInstruction startOfMethod;
73
74    public MethodAnalyzer(ClassDataItem.EncodedMethod encodedMethod, boolean deodex) {
75        if (encodedMethod == null) {
76            throw new IllegalArgumentException("encodedMethod cannot be null");
77        }
78        if (encodedMethod.codeItem == null || encodedMethod.codeItem.getInstructions().length == 0) {
79            throw new IllegalArgumentException("The method has no code");
80        }
81        this.encodedMethod = encodedMethod;
82
83        if (deodex) {
84            this.deodexUtil = new DeodexUtil(encodedMethod.method.getDexFile());
85        } else {
86            this.deodexUtil = null;
87        }
88
89        //override AnalyzedInstruction and provide custom implementations of some of the methods, so that we don't
90        //have to handle the case this special case of instruction being null, in the main class
91        startOfMethod = new AnalyzedInstruction(null, -1, encodedMethod.codeItem.getRegisterCount()) {
92            public boolean setsRegister() {
93                return false;
94            }
95
96            @Override
97            public boolean setsWideRegister() {
98                return false;
99            }
100
101            @Override
102            public boolean setsRegister(int registerNumber) {
103                return false;
104            }
105
106            @Override
107            public int getDestinationRegister() {
108                assert false;
109                return -1;
110            };
111        };
112
113        buildInstructionList();
114
115        analyzedInstructions = new BitSet(instructions.size());
116    }
117
118    public boolean isAnalyzed() {
119        return analyzerState >= ANALYZED;
120    }
121
122    public boolean isVerified() {
123        return analyzerState == VERIFIED;
124    }
125
126    public void analyze() {
127        assert encodedMethod != null;
128        assert encodedMethod.codeItem != null;
129
130        if (analyzerState >= ANALYZED) {
131            //the instructions have already been analyzed, so there is nothing to do
132            return;
133        }
134
135        CodeItem codeItem = encodedMethod.codeItem;
136        MethodIdItem methodIdItem = encodedMethod.method;
137
138        int totalRegisters = codeItem.getRegisterCount();
139        int parameterRegisters = methodIdItem.getPrototype().getParameterRegisterCount();
140
141        int nonParameterRegisters = totalRegisters - parameterRegisters;
142
143        for (AnalyzedInstruction instruction: instructions.getValues()) {
144            instruction.dead = true;
145        }
146
147        //if this isn't a static method, determine which register is the "this" register and set the type to the
148        //current class
149        if ((encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) == 0) {
150            nonParameterRegisters--;
151            int thisRegister = totalRegisters - parameterRegisters - 1;
152
153            //if this is a constructor, then set the "this" register to an uninitialized reference of the current class
154            if ((encodedMethod.accessFlags & AccessFlags.CONSTRUCTOR.getValue()) != 0) {
155                setPostRegisterTypeAndPropagateChanges(startOfMethod, thisRegister,
156                        RegisterType.getRegisterType(RegisterType.Category.UninitThis,
157                            ClassPath.getClassDef(methodIdItem.getContainingClass())));
158            } else {
159                setPostRegisterTypeAndPropagateChanges(startOfMethod, thisRegister,
160                        RegisterType.getRegisterType(RegisterType.Category.Reference,
161                            ClassPath.getClassDef(methodIdItem.getContainingClass())));
162            }
163        }
164
165        TypeListItem parameters = methodIdItem.getPrototype().getParameters();
166        if (parameters != null) {
167            RegisterType[] parameterTypes = getParameterTypes(parameters, parameterRegisters);
168            for (int i=0; i<parameterTypes.length; i++) {
169                RegisterType registerType = parameterTypes[i];
170                int registerNum = (totalRegisters - parameterRegisters) + i;
171                setPostRegisterTypeAndPropagateChanges(startOfMethod, registerNum, registerType);
172            }
173        }
174
175        RegisterType uninit = RegisterType.getRegisterType(RegisterType.Category.Uninit, null);
176        for (int i=0; i<nonParameterRegisters; i++) {
177            setPostRegisterTypeAndPropagateChanges(startOfMethod, i, uninit);
178        }
179
180        BitSet instructionsToAnalyze = new BitSet(instructions.size());
181
182        //make sure all of the "first instructions" are marked for processing
183        for (AnalyzedInstruction successor: startOfMethod.successors) {
184            instructionsToAnalyze.set(successor.instructionIndex);
185        }
186
187        BitSet undeodexedInstructions = new BitSet(instructions.size());
188
189        do {
190            boolean didSomething = false;
191
192            while (!instructionsToAnalyze.isEmpty()) {
193                for(int i=instructionsToAnalyze.nextSetBit(0); i>=0; i=instructionsToAnalyze.nextSetBit(i+1)) {
194                    instructionsToAnalyze.clear(i);
195                    if (analyzedInstructions.get(i)) {
196                        continue;
197                    }
198                    AnalyzedInstruction instructionToAnalyze = instructions.valueAt(i);
199                    instructionToAnalyze.dead = false;
200                    try {
201                        if (instructionToAnalyze.originalInstruction.opcode.odexOnly()) {
202                            //if we had deodexed an odex instruction in a previous pass, we might have more specific
203                            //register information now, so let's restore the original odexed instruction and
204                            //re-deodex it
205                            instructionToAnalyze.restoreOdexedInstruction();
206                        }
207
208                        if (!analyzeInstruction(instructionToAnalyze)) {
209                            undeodexedInstructions.set(i);
210                            continue;
211                        } else {
212                            didSomething = true;
213                            undeodexedInstructions.clear(i);
214                        }
215                    } catch (ValidationException ex) {
216                        this.validationException = ex;
217                        int codeAddress = getInstructionAddress(instructionToAnalyze);
218                        ex.setCodeAddress(codeAddress);
219                        ex.addContext(String.format("opcode: %s", instructionToAnalyze.instruction.opcode.name));
220                        ex.addContext(String.format("CodeAddress: %d", codeAddress));
221                        ex.addContext(String.format("Method: %s", encodedMethod.method.getMethodString()));
222                        break;
223                    }
224
225                    analyzedInstructions.set(instructionToAnalyze.getInstructionIndex());
226
227                    for (AnalyzedInstruction successor: instructionToAnalyze.successors) {
228                        instructionsToAnalyze.set(successor.getInstructionIndex());
229                    }
230                }
231                if (validationException != null) {
232                    break;
233                }
234            }
235
236            if (!didSomething) {
237                break;
238            }
239
240            if (!undeodexedInstructions.isEmpty()) {
241                for (int i=undeodexedInstructions.nextSetBit(0); i>=0; i=undeodexedInstructions.nextSetBit(i+1)) {
242                    instructionsToAnalyze.set(i);
243                }
244            }
245        } while (true);
246
247        for (int i=0; i<instructions.size(); i++) {
248            AnalyzedInstruction instruction = instructions.valueAt(i);
249
250            int objectRegisterNumber;
251            switch (instruction.getInstruction().getFormat()) {
252                case Format22cs:
253                    objectRegisterNumber = ((Instruction22cs)instruction.instruction).getRegisterB();
254                    break;
255                case Format35mi:
256                case Format35ms:
257                    objectRegisterNumber = ((FiveRegisterInstruction)instruction.instruction).getRegisterD();
258                    break;
259                case Format3rmi:
260                case Format3rms:
261                    objectRegisterNumber = ((RegisterRangeInstruction)instruction.instruction).getStartRegister();
262                    break;
263                default:
264                    continue;
265            }
266
267            instruction.setDeodexedInstruction(new UnresolvedOdexInstruction(instruction.instruction,
268                    objectRegisterNumber));
269        }
270
271        analyzerState = ANALYZED;
272    }
273
274    public void verify() {
275        if (analyzerState < ANALYZED) {
276            throw new ExceptionWithContext("You must call analyze() before calling verify().");
277        }
278
279        if (analyzerState == VERIFIED) {
280            //we've already verified the bytecode. nothing to do
281            return;
282        }
283
284        BitSet instructionsToVerify = new BitSet(instructions.size());
285        BitSet verifiedInstructions = new BitSet(instructions.size());
286
287        //make sure all of the "first instructions" are marked for processing
288        for (AnalyzedInstruction successor: startOfMethod.successors) {
289            instructionsToVerify.set(successor.instructionIndex);
290        }
291
292        while (!instructionsToVerify.isEmpty()) {
293            for (int i=instructionsToVerify.nextSetBit(0); i>=0; i=instructionsToVerify.nextSetBit(i+1)) {
294                instructionsToVerify.clear(i);
295                if (verifiedInstructions.get(i)) {
296                    continue;
297                }
298                AnalyzedInstruction instructionToVerify = instructions.valueAt(i);
299                try {
300                    verifyInstruction(instructionToVerify);
301                } catch (ValidationException ex) {
302                    this.validationException = ex;
303                    int codeAddress = getInstructionAddress(instructionToVerify);
304                    ex.setCodeAddress(codeAddress);
305                    ex.addContext(String.format("opcode: %s", instructionToVerify.instruction.opcode.name));
306                    ex.addContext(String.format("CodeAddress: %d", codeAddress));
307                    ex.addContext(String.format("Method: %s", encodedMethod.method.getMethodString()));
308                    break;
309                }
310
311                verifiedInstructions.set(instructionToVerify.getInstructionIndex());
312
313                for (AnalyzedInstruction successor: instructionToVerify.successors) {
314                    instructionsToVerify.set(successor.getInstructionIndex());
315                }
316            }
317            if (validationException != null) {
318                break;
319            }
320        }
321
322        analyzerState = VERIFIED;
323    }
324
325    private int getThisRegister() {
326        assert (encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) == 0;
327
328        CodeItem codeItem = encodedMethod.codeItem;
329        assert codeItem != null;
330
331        MethodIdItem methodIdItem = encodedMethod.method;
332        assert methodIdItem != null;
333
334        int totalRegisters = codeItem.getRegisterCount();
335        if (totalRegisters == 0) {
336            throw new ValidationException("A non-static method must have at least 1 register");
337        }
338
339        int parameterRegisters = methodIdItem.getPrototype().getParameterRegisterCount();
340
341        return totalRegisters - parameterRegisters - 1;
342    }
343
344    private boolean isInstanceConstructor() {
345        return (encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) == 0 &&
346               (encodedMethod.accessFlags & AccessFlags.CONSTRUCTOR.getValue()) != 0;
347    }
348
349    private boolean isStaticConstructor() {
350        return (encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) != 0 &&
351               (encodedMethod.accessFlags & AccessFlags.CONSTRUCTOR.getValue()) != 0;
352    }
353
354    public AnalyzedInstruction getStartOfMethod() {
355        return startOfMethod;
356    }
357
358    /**
359     * @return a read-only list containing the instructions for tihs method.
360     */
361    public List<AnalyzedInstruction> getInstructions() {
362        return instructions.getValues();
363    }
364
365    public ClassDataItem.EncodedMethod getMethod() {
366        return this.encodedMethod;
367    }
368
369    public ValidationException getValidationException() {
370        return validationException;
371    }
372
373    private static RegisterType[] getParameterTypes(TypeListItem typeListItem, int parameterRegisterCount) {
374        assert typeListItem != null;
375        assert parameterRegisterCount == typeListItem.getRegisterCount();
376
377        RegisterType[] registerTypes = new RegisterType[parameterRegisterCount];
378
379        int registerNum = 0;
380        for (TypeIdItem type: typeListItem.getTypes()) {
381            if (type.getRegisterCount() == 2) {
382                registerTypes[registerNum++] = RegisterType.getWideRegisterTypeForTypeIdItem(type, true);
383                registerTypes[registerNum++] = RegisterType.getWideRegisterTypeForTypeIdItem(type, false);
384            } else {
385                registerTypes[registerNum++] = RegisterType.getRegisterTypeForTypeIdItem(type);
386            }
387        }
388
389        return registerTypes;
390    }
391
392    public int getInstructionAddress(AnalyzedInstruction instruction) {
393        return instructions.keyAt(instruction.instructionIndex);
394    }
395
396    private void setDestinationRegisterTypeAndPropagateChanges(AnalyzedInstruction analyzedInstruction,
397                                                               RegisterType registerType) {
398        setPostRegisterTypeAndPropagateChanges(analyzedInstruction, analyzedInstruction.getDestinationRegister(),
399                registerType);
400    }
401
402    private void setPostRegisterTypeAndPropagateChanges(AnalyzedInstruction analyzedInstruction, int registerNumber,
403                                                RegisterType registerType) {
404
405        BitSet changedInstructions = new BitSet(instructions.size());
406
407        if (!analyzedInstruction.setPostRegisterType(registerNumber, registerType)) {
408            return;
409        }
410
411        propagateRegisterToSuccessors(analyzedInstruction, registerNumber, changedInstructions);
412
413        //Using a for loop inside the while loop optimizes for the common case of the successors of an instruction
414        //occurring after the instruction. Any successors that occur prior to the instruction will be picked up on
415        //the next iteration of the while loop.
416        //This could also be done recursively, but in large methods it would likely cause very deep recursion,
417        //which requires the user to specify a larger stack size. This isn't really a problem, but it is slightly
418        //annoying.
419        while (!changedInstructions.isEmpty()) {
420            for (int instructionIndex=changedInstructions.nextSetBit(0);
421                     instructionIndex>=0;
422                     instructionIndex=changedInstructions.nextSetBit(instructionIndex+1)) {
423
424                changedInstructions.clear(instructionIndex);
425
426                propagateRegisterToSuccessors(instructions.valueAt(instructionIndex), registerNumber,
427                        changedInstructions);
428            }
429        }
430
431        if (registerType.category == RegisterType.Category.LongLo) {
432            checkWidePair(registerNumber, analyzedInstruction);
433            setPostRegisterTypeAndPropagateChanges(analyzedInstruction, registerNumber+1,
434                    RegisterType.getRegisterType(RegisterType.Category.LongHi, null));
435        } else if (registerType.category == RegisterType.Category.DoubleLo) {
436            checkWidePair(registerNumber, analyzedInstruction);
437            setPostRegisterTypeAndPropagateChanges(analyzedInstruction, registerNumber+1,
438                    RegisterType.getRegisterType(RegisterType.Category.DoubleHi, null));
439        }
440    }
441
442    private void propagateRegisterToSuccessors(AnalyzedInstruction instruction, int registerNumber,
443                                               BitSet changedInstructions) {
444        RegisterType postRegisterType = instruction.getPostInstructionRegisterType(registerNumber);
445        for (AnalyzedInstruction successor: instruction.successors) {
446            if (successor.mergeRegister(registerNumber, postRegisterType, analyzedInstructions)) {
447                changedInstructions.set(successor.instructionIndex);
448            }
449        }
450    }
451
452    private void buildInstructionList() {
453        assert encodedMethod != null;
454        assert encodedMethod.codeItem != null;
455        int registerCount = encodedMethod.codeItem.getRegisterCount();
456
457        Instruction[] insns = encodedMethod.codeItem.getInstructions();
458
459        instructions = new SparseArray<AnalyzedInstruction>(insns.length);
460
461        //first, create all the instructions and populate the instructionAddresses array
462        int currentCodeAddress = 0;
463        for (int i=0; i<insns.length; i++) {
464            instructions.append(currentCodeAddress, new AnalyzedInstruction(insns[i], i, registerCount));
465            assert instructions.indexOfKey(currentCodeAddress) == i;
466            currentCodeAddress += insns[i].getSize(currentCodeAddress);
467        }
468
469        //next, populate the exceptionHandlers array. The array item for each instruction that can throw an exception
470        //and is covered by a try block should be set to a list of the first instructions of each exception handler
471        //for the try block covering the instruction
472        CodeItem.TryItem[] tries = encodedMethod.codeItem.getTries();
473        int triesIndex = 0;
474        CodeItem.TryItem currentTry = null;
475        AnalyzedInstruction[] currentExceptionHandlers = null;
476        AnalyzedInstruction[][] exceptionHandlers = new AnalyzedInstruction[insns.length][];
477
478        if (tries != null) {
479            for (int i=0; i<instructions.size(); i++) {
480                AnalyzedInstruction instruction = instructions.valueAt(i);
481                Opcode instructionOpcode = instruction.instruction.opcode;
482                currentCodeAddress = getInstructionAddress(instruction);
483
484                //check if we have gone past the end of the current try
485                if (currentTry != null) {
486                    if (currentTry.getStartCodeAddress() + currentTry.getTryLength() <= currentCodeAddress) {
487                        currentTry = null;
488                        triesIndex++;
489                    }
490                }
491
492                //check if the next try is applicable yet
493                if (currentTry == null && triesIndex < tries.length) {
494                    CodeItem.TryItem tryItem = tries[triesIndex];
495                    if (tryItem.getStartCodeAddress() <= currentCodeAddress) {
496                        assert(tryItem.getStartCodeAddress() + tryItem.getTryLength() > currentCodeAddress);
497
498                        currentTry = tryItem;
499
500                        currentExceptionHandlers = buildExceptionHandlerArray(tryItem);
501                    }
502                }
503
504                //if we're inside a try block, and the instruction can throw an exception, then add the exception handlers
505                //for the current instruction
506                if (currentTry != null && instructionOpcode.canThrow()) {
507                    exceptionHandlers[i] = currentExceptionHandlers;
508                }
509            }
510        }
511
512        //finally, populate the successors and predecessors for each instruction. We start at the fake "StartOfMethod"
513        //instruction and follow the execution path. Any unreachable code won't have any predecessors or successors,
514        //and no reachable code will have an unreachable predessor or successor
515        assert instructions.size() > 0;
516        BitSet instructionsToProcess = new BitSet(insns.length);
517
518        addPredecessorSuccessor(startOfMethod, instructions.valueAt(0), exceptionHandlers, instructionsToProcess);
519        while (!instructionsToProcess.isEmpty()) {
520            int currentInstructionIndex = instructionsToProcess.nextSetBit(0);
521            instructionsToProcess.clear(currentInstructionIndex);
522
523            AnalyzedInstruction instruction = instructions.valueAt(currentInstructionIndex);
524            Opcode instructionOpcode = instruction.instruction.opcode;
525            int instructionCodeAddress = getInstructionAddress(instruction);
526
527            if (instruction.instruction.opcode.canContinue()) {
528                if (instruction.instruction.opcode != Opcode.NOP ||
529                    !instruction.instruction.getFormat().variableSizeFormat) {
530
531                    if (currentInstructionIndex == instructions.size() - 1) {
532                        throw new ValidationException("Execution can continue past the last instruction");
533                    }
534
535                    AnalyzedInstruction nextInstruction = instructions.valueAt(currentInstructionIndex+1);
536                    addPredecessorSuccessor(instruction, nextInstruction, exceptionHandlers, instructionsToProcess);
537                }
538            }
539
540            if (instruction.instruction instanceof OffsetInstruction) {
541                OffsetInstruction offsetInstruction = (OffsetInstruction)instruction.instruction;
542
543                if (instructionOpcode == Opcode.PACKED_SWITCH || instructionOpcode == Opcode.SPARSE_SWITCH) {
544                    MultiOffsetInstruction switchDataInstruction =
545                            (MultiOffsetInstruction)instructions.get(instructionCodeAddress +
546                                    offsetInstruction.getTargetAddressOffset()).instruction;
547                    for (int targetAddressOffset: switchDataInstruction.getTargets()) {
548                        AnalyzedInstruction targetInstruction = instructions.get(instructionCodeAddress +
549                                targetAddressOffset);
550
551                        addPredecessorSuccessor(instruction, targetInstruction, exceptionHandlers,
552                                instructionsToProcess);
553                    }
554                } else {
555                    int targetAddressOffset = offsetInstruction.getTargetAddressOffset();
556                    AnalyzedInstruction targetInstruction = instructions.get(instructionCodeAddress +
557                            targetAddressOffset);
558                    addPredecessorSuccessor(instruction, targetInstruction, exceptionHandlers, instructionsToProcess);
559                }
560            }
561        }
562    }
563
564    private void addPredecessorSuccessor(AnalyzedInstruction predecessor, AnalyzedInstruction successor,
565                                                AnalyzedInstruction[][] exceptionHandlers,
566                                                BitSet instructionsToProcess) {
567        addPredecessorSuccessor(predecessor, successor, exceptionHandlers, instructionsToProcess, false);
568    }
569
570    private void addPredecessorSuccessor(AnalyzedInstruction predecessor, AnalyzedInstruction successor,
571                                                AnalyzedInstruction[][] exceptionHandlers,
572                                                BitSet instructionsToProcess, boolean allowMoveException) {
573
574        if (!allowMoveException && successor.instruction.opcode == Opcode.MOVE_EXCEPTION) {
575            throw new ValidationException("Execution can pass from the " + predecessor.instruction.opcode.name +
576                    " instruction at code address 0x" + Integer.toHexString(getInstructionAddress(predecessor)) +
577                    " to the move-exception instruction at address 0x" +
578                    Integer.toHexString(getInstructionAddress(successor)));
579        }
580
581        if (!successor.addPredecessor(predecessor)) {
582            return;
583        }
584
585        predecessor.addSuccessor(successor);
586        instructionsToProcess.set(successor.getInstructionIndex());
587
588
589        //if the successor can throw an instruction, then we need to add the exception handlers as additional
590        //successors to the predecessor (and then apply this same logic recursively if needed)
591        //Technically, we should handle the monitor-exit instruction as a special case. The exception is actually
592        //thrown *after* the instruction executes, instead of "before" the instruction executes, lke for any other
593        //instruction. But since it doesn't modify any registers, we can treat it like any other instruction.
594        AnalyzedInstruction[] exceptionHandlersForSuccessor = exceptionHandlers[successor.instructionIndex];
595        if (exceptionHandlersForSuccessor != null) {
596            //the item for this instruction in exceptionHandlersForSuccessor should only be set if this instruction
597            //can throw an exception
598            assert successor.instruction.opcode.canThrow();
599
600            for (AnalyzedInstruction exceptionHandler: exceptionHandlersForSuccessor) {
601                addPredecessorSuccessor(predecessor, exceptionHandler, exceptionHandlers, instructionsToProcess, true);
602            }
603        }
604    }
605
606    private AnalyzedInstruction[] buildExceptionHandlerArray(CodeItem.TryItem tryItem) {
607        int exceptionHandlerCount = tryItem.encodedCatchHandler.handlers.length;
608        int catchAllHandler = tryItem.encodedCatchHandler.getCatchAllHandlerAddress();
609        if (catchAllHandler != -1) {
610            exceptionHandlerCount++;
611        }
612
613        AnalyzedInstruction[] exceptionHandlers = new AnalyzedInstruction[exceptionHandlerCount];
614        for (int i=0; i<tryItem.encodedCatchHandler.handlers.length; i++) {
615            exceptionHandlers[i] = instructions.get(tryItem.encodedCatchHandler.handlers[i].getHandlerAddress());
616        }
617
618        if (catchAllHandler != -1) {
619            exceptionHandlers[exceptionHandlers.length - 1] = instructions.get(catchAllHandler);
620        }
621
622        return exceptionHandlers;
623    }
624
625    /**
626     * @return false if analyzedInstruction is an odex instruction that couldn't be deodexed, due to its
627     * object register being null
628     */
629    private boolean analyzeInstruction(AnalyzedInstruction analyzedInstruction) {
630        Instruction instruction = analyzedInstruction.instruction;
631
632        switch (instruction.opcode) {
633            case NOP:
634                return true;
635            case MOVE:
636            case MOVE_FROM16:
637            case MOVE_16:
638            case MOVE_WIDE:
639            case MOVE_WIDE_FROM16:
640            case MOVE_WIDE_16:
641            case MOVE_OBJECT:
642            case MOVE_OBJECT_FROM16:
643            case MOVE_OBJECT_16:
644                analyzeMove(analyzedInstruction);
645                return true;
646            case MOVE_RESULT:
647            case MOVE_RESULT_WIDE:
648            case MOVE_RESULT_OBJECT:
649                analyzeMoveResult(analyzedInstruction);
650                return true;
651            case MOVE_EXCEPTION:
652                analyzeMoveException(analyzedInstruction);
653                return true;
654            case RETURN_VOID:
655            case RETURN:
656            case RETURN_WIDE:
657            case RETURN_OBJECT:
658                return true;
659            case CONST_4:
660            case CONST_16:
661            case CONST:
662                analyzeConst(analyzedInstruction);
663                return true;
664            case CONST_HIGH16:
665                analyzeConstHigh16(analyzedInstruction);
666                return true;
667            case CONST_WIDE_16:
668            case CONST_WIDE_32:
669            case CONST_WIDE:
670            case CONST_WIDE_HIGH16:
671                analyzeWideConst(analyzedInstruction);
672                return true;
673            case CONST_STRING:
674            case CONST_STRING_JUMBO:
675                analyzeConstString(analyzedInstruction);
676                return true;
677            case CONST_CLASS:
678                analyzeConstClass(analyzedInstruction);
679                return true;
680            case MONITOR_ENTER:
681            case MONITOR_EXIT:
682                return true;
683            case CHECK_CAST:
684                analyzeCheckCast(analyzedInstruction);
685                return true;
686            case INSTANCE_OF:
687                analyzeInstanceOf(analyzedInstruction);
688                return true;
689            case ARRAY_LENGTH:
690                analyzeArrayLength(analyzedInstruction);
691                return true;
692            case NEW_INSTANCE:
693                analyzeNewInstance(analyzedInstruction);
694                return true;
695            case NEW_ARRAY:
696                analyzeNewArray(analyzedInstruction);
697                return true;
698            case FILLED_NEW_ARRAY:
699            case FILLED_NEW_ARRAY_RANGE:
700                return true;
701            case FILL_ARRAY_DATA:
702                analyzeArrayDataOrSwitch(analyzedInstruction);
703            case THROW:
704            case GOTO:
705            case GOTO_16:
706            case GOTO_32:
707                return true;
708            case PACKED_SWITCH:
709            case SPARSE_SWITCH:
710                analyzeArrayDataOrSwitch(analyzedInstruction);
711                return true;
712            case CMPL_FLOAT:
713            case CMPG_FLOAT:
714            case CMPL_DOUBLE:
715            case CMPG_DOUBLE:
716            case CMP_LONG:
717                analyzeFloatWideCmp(analyzedInstruction);
718                return true;
719            case IF_EQ:
720            case IF_NE:
721            case IF_LT:
722            case IF_GE:
723            case IF_GT:
724            case IF_LE:
725            case IF_EQZ:
726            case IF_NEZ:
727            case IF_LTZ:
728            case IF_GEZ:
729            case IF_GTZ:
730            case IF_LEZ:
731                return true;
732            case AGET:
733                analyze32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Integer);
734                return true;
735            case AGET_BOOLEAN:
736                analyze32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Boolean);
737                return true;
738            case AGET_BYTE:
739                analyze32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Byte);
740                return true;
741            case AGET_CHAR:
742                analyze32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Char);
743                return true;
744            case AGET_SHORT:
745                analyze32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Short);
746                return true;
747            case AGET_WIDE:
748                analyzeAgetWide(analyzedInstruction);
749                return true;
750            case AGET_OBJECT:
751                analyzeAgetObject(analyzedInstruction);
752                return true;
753            case APUT:
754            case APUT_BOOLEAN:
755            case APUT_BYTE:
756            case APUT_CHAR:
757            case APUT_SHORT:
758            case APUT_WIDE:
759            case APUT_OBJECT:
760                return true;
761            case IGET:
762                analyze32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Integer);
763                return true;
764            case IGET_BOOLEAN:
765                analyze32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Boolean);
766                return true;
767            case IGET_BYTE:
768                analyze32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Byte);
769                return true;
770            case IGET_CHAR:
771                analyze32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Char);
772                return true;
773            case IGET_SHORT:
774                analyze32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Short);
775                return true;
776            case IGET_WIDE:
777            case IGET_OBJECT:
778                analyzeIgetWideObject(analyzedInstruction);
779                return true;
780            case IPUT:
781            case IPUT_BOOLEAN:
782            case IPUT_BYTE:
783            case IPUT_CHAR:
784            case IPUT_SHORT:
785            case IPUT_WIDE:
786            case IPUT_OBJECT:
787                return true;
788            case SGET:
789                analyze32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Integer);
790                return true;
791            case SGET_BOOLEAN:
792                analyze32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Boolean);
793                return true;
794            case SGET_BYTE:
795                analyze32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Byte);
796                return true;
797            case SGET_CHAR:
798                analyze32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Char);
799                return true;
800            case SGET_SHORT:
801                analyze32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Short);
802                return true;
803            case SGET_WIDE:
804            case SGET_OBJECT:
805                analyzeSgetWideObject(analyzedInstruction);
806                return true;
807            case SPUT:
808            case SPUT_BOOLEAN:
809            case SPUT_BYTE:
810            case SPUT_CHAR:
811            case SPUT_SHORT:
812            case SPUT_WIDE:
813            case SPUT_OBJECT:
814                return true;
815            case INVOKE_VIRTUAL:
816            case INVOKE_SUPER:
817                return true;
818            case INVOKE_DIRECT:
819                analyzeInvokeDirect(analyzedInstruction);
820                return true;
821            case INVOKE_STATIC:
822            case INVOKE_INTERFACE:
823            case INVOKE_VIRTUAL_RANGE:
824            case INVOKE_SUPER_RANGE:
825                return true;
826            case INVOKE_DIRECT_RANGE:
827                analyzeInvokeDirectRange(analyzedInstruction);
828                return true;
829            case INVOKE_STATIC_RANGE:
830            case INVOKE_INTERFACE_RANGE:
831                return true;
832            case NEG_INT:
833            case NOT_INT:
834                analyzeUnaryOp(analyzedInstruction, RegisterType.Category.Integer);
835                return true;
836            case NEG_LONG:
837            case NOT_LONG:
838                analyzeUnaryOp(analyzedInstruction, RegisterType.Category.LongLo);
839                return true;
840            case NEG_FLOAT:
841                analyzeUnaryOp(analyzedInstruction, RegisterType.Category.Float);
842                return true;
843            case NEG_DOUBLE:
844                analyzeUnaryOp(analyzedInstruction, RegisterType.Category.DoubleLo);
845                return true;
846            case INT_TO_LONG:
847                analyzeUnaryOp(analyzedInstruction, RegisterType.Category.LongLo);
848                return true;
849            case INT_TO_FLOAT:
850                analyzeUnaryOp(analyzedInstruction, RegisterType.Category.Float);
851                return true;
852            case INT_TO_DOUBLE:
853                analyzeUnaryOp(analyzedInstruction, RegisterType.Category.DoubleLo);
854                return true;
855            case LONG_TO_INT:
856            case DOUBLE_TO_INT:
857                analyzeUnaryOp(analyzedInstruction, RegisterType.Category.Integer);
858                return true;
859            case LONG_TO_FLOAT:
860            case DOUBLE_TO_FLOAT:
861                analyzeUnaryOp(analyzedInstruction, RegisterType.Category.Float);
862                return true;
863            case LONG_TO_DOUBLE:
864                analyzeUnaryOp(analyzedInstruction, RegisterType.Category.DoubleLo);
865                return true;
866            case FLOAT_TO_INT:
867                analyzeUnaryOp(analyzedInstruction, RegisterType.Category.Integer);
868                return true;
869            case FLOAT_TO_LONG:
870                analyzeUnaryOp(analyzedInstruction, RegisterType.Category.LongLo);
871                return true;
872            case FLOAT_TO_DOUBLE:
873                analyzeUnaryOp(analyzedInstruction, RegisterType.Category.DoubleLo);
874                return true;
875            case DOUBLE_TO_LONG:
876                analyzeUnaryOp(analyzedInstruction, RegisterType.Category.LongLo);
877                return true;
878            case INT_TO_BYTE:
879                analyzeUnaryOp(analyzedInstruction, RegisterType.Category.Byte);
880                return true;
881            case INT_TO_CHAR:
882                analyzeUnaryOp(analyzedInstruction, RegisterType.Category.Char);
883                return true;
884            case INT_TO_SHORT:
885                analyzeUnaryOp(analyzedInstruction, RegisterType.Category.Short);
886                return true;
887            case ADD_INT:
888            case SUB_INT:
889            case MUL_INT:
890            case DIV_INT:
891            case REM_INT:
892            case SHL_INT:
893            case SHR_INT:
894            case USHR_INT:
895                analyzeBinaryOp(analyzedInstruction, RegisterType.Category.Integer, false);
896                return true;
897            case AND_INT:
898            case OR_INT:
899            case XOR_INT:
900                analyzeBinaryOp(analyzedInstruction, RegisterType.Category.Integer, true);
901                return true;
902            case ADD_LONG:
903            case SUB_LONG:
904            case MUL_LONG:
905            case DIV_LONG:
906            case REM_LONG:
907            case AND_LONG:
908            case OR_LONG:
909            case XOR_LONG:
910            case SHL_LONG:
911            case SHR_LONG:
912            case USHR_LONG:
913                analyzeBinaryOp(analyzedInstruction, RegisterType.Category.LongLo, false);
914                return true;
915            case ADD_FLOAT:
916            case SUB_FLOAT:
917            case MUL_FLOAT:
918            case DIV_FLOAT:
919            case REM_FLOAT:
920                analyzeBinaryOp(analyzedInstruction, RegisterType.Category.Float, false);
921                return true;
922            case ADD_DOUBLE:
923            case SUB_DOUBLE:
924            case MUL_DOUBLE:
925            case DIV_DOUBLE:
926            case REM_DOUBLE:
927                analyzeBinaryOp(analyzedInstruction, RegisterType.Category.DoubleLo, false);
928                return true;
929            case ADD_INT_2ADDR:
930            case SUB_INT_2ADDR:
931            case MUL_INT_2ADDR:
932            case DIV_INT_2ADDR:
933            case REM_INT_2ADDR:
934            case SHL_INT_2ADDR:
935            case SHR_INT_2ADDR:
936            case USHR_INT_2ADDR:
937                analyzeBinary2AddrOp(analyzedInstruction, RegisterType.Category.Integer, false);
938                return true;
939            case AND_INT_2ADDR:
940            case OR_INT_2ADDR:
941            case XOR_INT_2ADDR:
942                analyzeBinary2AddrOp(analyzedInstruction, RegisterType.Category.Integer, true);
943                return true;
944            case ADD_LONG_2ADDR:
945            case SUB_LONG_2ADDR:
946            case MUL_LONG_2ADDR:
947            case DIV_LONG_2ADDR:
948            case REM_LONG_2ADDR:
949            case AND_LONG_2ADDR:
950            case OR_LONG_2ADDR:
951            case XOR_LONG_2ADDR:
952            case SHL_LONG_2ADDR:
953            case SHR_LONG_2ADDR:
954            case USHR_LONG_2ADDR:
955                analyzeBinary2AddrOp(analyzedInstruction, RegisterType.Category.LongLo, false);
956                return true;
957            case ADD_FLOAT_2ADDR:
958            case SUB_FLOAT_2ADDR:
959            case MUL_FLOAT_2ADDR:
960            case DIV_FLOAT_2ADDR:
961            case REM_FLOAT_2ADDR:
962                analyzeBinary2AddrOp(analyzedInstruction, RegisterType.Category.Float, false);
963                return true;
964            case ADD_DOUBLE_2ADDR:
965            case SUB_DOUBLE_2ADDR:
966            case MUL_DOUBLE_2ADDR:
967            case DIV_DOUBLE_2ADDR:
968            case REM_DOUBLE_2ADDR:
969                analyzeBinary2AddrOp(analyzedInstruction, RegisterType.Category.DoubleLo, false);
970                return true;
971            case ADD_INT_LIT16:
972            case RSUB_INT:
973            case MUL_INT_LIT16:
974            case DIV_INT_LIT16:
975            case REM_INT_LIT16:
976                analyzeLiteralBinaryOp(analyzedInstruction, RegisterType.Category.Integer, false);
977                return true;
978            case AND_INT_LIT16:
979            case OR_INT_LIT16:
980            case XOR_INT_LIT16:
981                analyzeLiteralBinaryOp(analyzedInstruction, RegisterType.Category.Integer, true);
982                return true;
983            case ADD_INT_LIT8:
984            case RSUB_INT_LIT8:
985            case MUL_INT_LIT8:
986            case DIV_INT_LIT8:
987            case REM_INT_LIT8:
988            case SHL_INT_LIT8:
989                analyzeLiteralBinaryOp(analyzedInstruction, RegisterType.Category.Integer, false);
990                return true;
991            case AND_INT_LIT8:
992            case OR_INT_LIT8:
993            case XOR_INT_LIT8:
994                analyzeLiteralBinaryOp(analyzedInstruction, RegisterType.Category.Integer, true);
995                return true;
996            case SHR_INT_LIT8:
997                analyzeLiteralBinaryOp(analyzedInstruction, getDestTypeForLiteralShiftRight(analyzedInstruction, true),
998                        false);
999                return true;
1000            case USHR_INT_LIT8:
1001                analyzeLiteralBinaryOp(analyzedInstruction, getDestTypeForLiteralShiftRight(analyzedInstruction, false),
1002                        false);
1003                return true;
1004
1005            /*odexed instructions*/
1006            case IGET_VOLATILE:
1007            case IPUT_VOLATILE:
1008            case SGET_VOLATILE:
1009            case SPUT_VOLATILE:
1010            case IGET_OBJECT_VOLATILE:
1011            case IGET_WIDE_VOLATILE:
1012            case IPUT_WIDE_VOLATILE:
1013            case SGET_WIDE_VOLATILE:
1014            case SPUT_WIDE_VOLATILE:
1015                analyzePutGetVolatile(analyzedInstruction);
1016                return true;
1017            case THROW_VERIFICATION_ERROR:
1018                return true;
1019            case EXECUTE_INLINE:
1020                analyzeExecuteInline(analyzedInstruction);
1021                return true;
1022            case EXECUTE_INLINE_RANGE:
1023                analyzeExecuteInlineRange(analyzedInstruction);
1024                return true;
1025            case INVOKE_DIRECT_EMPTY:
1026                analyzeInvokeDirectEmpty(analyzedInstruction);
1027                return true;
1028            case IGET_QUICK:
1029            case IGET_WIDE_QUICK:
1030            case IGET_OBJECT_QUICK:
1031            case IPUT_QUICK:
1032            case IPUT_WIDE_QUICK:
1033            case IPUT_OBJECT_QUICK:
1034                return analyzeIputIgetQuick(analyzedInstruction);
1035            case INVOKE_VIRTUAL_QUICK:
1036                return analyzeInvokeVirtualQuick(analyzedInstruction, false, false);
1037            case INVOKE_SUPER_QUICK:
1038                return analyzeInvokeVirtualQuick(analyzedInstruction, true, false);
1039            case INVOKE_VIRTUAL_QUICK_RANGE:
1040                return analyzeInvokeVirtualQuick(analyzedInstruction, false, true);
1041            case INVOKE_SUPER_QUICK_RANGE:
1042                return analyzeInvokeVirtualQuick(analyzedInstruction, true, true);
1043            case IPUT_OBJECT_VOLATILE:
1044            case SGET_OBJECT_VOLATILE:
1045            case SPUT_OBJECT_VOLATILE:
1046                analyzePutGetVolatile(analyzedInstruction);
1047                return true;
1048            default:
1049                assert false;
1050                return true;
1051        }
1052    }
1053
1054
1055        private void verifyInstruction(AnalyzedInstruction analyzedInstruction) {
1056        Instruction instruction = analyzedInstruction.instruction;
1057
1058        switch (instruction.opcode) {
1059            case NOP:
1060                return;
1061            case MOVE:
1062            case MOVE_FROM16:
1063            case MOVE_16:
1064                verifyMove(analyzedInstruction, Primitive32BitCategories);
1065                return;
1066            case MOVE_WIDE:
1067            case MOVE_WIDE_FROM16:
1068            case MOVE_WIDE_16:
1069                verifyMove(analyzedInstruction, WideLowCategories);
1070                return;
1071            case MOVE_OBJECT:
1072            case MOVE_OBJECT_FROM16:
1073            case MOVE_OBJECT_16:
1074                verifyMove(analyzedInstruction, ReferenceOrUninitCategories);
1075                return;
1076            case MOVE_RESULT:
1077                verifyMoveResult(analyzedInstruction, Primitive32BitCategories);
1078                return;
1079            case MOVE_RESULT_WIDE:
1080                verifyMoveResult(analyzedInstruction, WideLowCategories);
1081                return;
1082            case MOVE_RESULT_OBJECT:
1083                verifyMoveResult(analyzedInstruction, ReferenceCategories);
1084                return;
1085            case MOVE_EXCEPTION:
1086                verifyMoveException(analyzedInstruction);
1087                return;
1088            case RETURN_VOID:
1089                verifyReturnVoid(analyzedInstruction);
1090                return;
1091            case RETURN:
1092                verifyReturn(analyzedInstruction, Primitive32BitCategories);
1093                return;
1094            case RETURN_WIDE:
1095                verifyReturn(analyzedInstruction, WideLowCategories);
1096                return;
1097            case RETURN_OBJECT:
1098                verifyReturn(analyzedInstruction, ReferenceCategories);
1099                return;
1100            case CONST_4:
1101            case CONST_16:
1102            case CONST:
1103            case CONST_HIGH16:
1104            case CONST_WIDE_16:
1105            case CONST_WIDE_32:
1106            case CONST_WIDE:
1107            case CONST_WIDE_HIGH16:
1108            case CONST_STRING:
1109            case CONST_STRING_JUMBO:
1110                return;
1111            case CONST_CLASS:
1112                verifyConstClass(analyzedInstruction);
1113                return;
1114            case MONITOR_ENTER:
1115            case MONITOR_EXIT:
1116                verifyMonitor(analyzedInstruction);
1117                return;
1118            case CHECK_CAST:
1119                verifyCheckCast(analyzedInstruction);
1120                return;
1121            case INSTANCE_OF:
1122                verifyInstanceOf(analyzedInstruction);
1123                return;
1124            case ARRAY_LENGTH:
1125                verifyArrayLength(analyzedInstruction);
1126                return;
1127            case NEW_INSTANCE:
1128                verifyNewInstance(analyzedInstruction);
1129                return;
1130            case NEW_ARRAY:
1131                verifyNewArray(analyzedInstruction);
1132                return;
1133            case FILLED_NEW_ARRAY:
1134                verifyFilledNewArray(analyzedInstruction);
1135                return;
1136            case FILLED_NEW_ARRAY_RANGE:
1137                verifyFilledNewArrayRange(analyzedInstruction);
1138                return;
1139            case FILL_ARRAY_DATA:
1140                verifyFillArrayData(analyzedInstruction);
1141                return;
1142            case THROW:
1143                verifyThrow(analyzedInstruction);
1144                return;
1145            case GOTO:
1146            case GOTO_16:
1147            case GOTO_32:
1148                return;
1149            case PACKED_SWITCH:
1150                verifySwitch(analyzedInstruction, Format.PackedSwitchData);
1151                return;
1152            case SPARSE_SWITCH:
1153                verifySwitch(analyzedInstruction, Format.SparseSwitchData);
1154                return;
1155            case CMPL_FLOAT:
1156            case CMPG_FLOAT:
1157                verifyFloatWideCmp(analyzedInstruction, Primitive32BitCategories);
1158                return;
1159            case CMPL_DOUBLE:
1160            case CMPG_DOUBLE:
1161            case CMP_LONG:
1162                verifyFloatWideCmp(analyzedInstruction, WideLowCategories);
1163                return;
1164            case IF_EQ:
1165            case IF_NE:
1166                verifyIfEqNe(analyzedInstruction);
1167                return;
1168            case IF_LT:
1169            case IF_GE:
1170            case IF_GT:
1171            case IF_LE:
1172                verifyIf(analyzedInstruction);
1173                return;
1174            case IF_EQZ:
1175            case IF_NEZ:
1176                verifyIfEqzNez(analyzedInstruction);
1177                return;
1178            case IF_LTZ:
1179            case IF_GEZ:
1180            case IF_GTZ:
1181            case IF_LEZ:
1182                verifyIfz(analyzedInstruction);
1183                return;
1184            case AGET:
1185                verify32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Integer);
1186                return;
1187            case AGET_BOOLEAN:
1188                verify32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Boolean);
1189                return;
1190            case AGET_BYTE:
1191                verify32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Byte);
1192                return;
1193            case AGET_CHAR:
1194                verify32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Char);
1195                return;
1196            case AGET_SHORT:
1197                verify32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Short);
1198                return;
1199            case AGET_WIDE:
1200                verifyAgetWide(analyzedInstruction);
1201                return;
1202            case AGET_OBJECT:
1203                verifyAgetObject(analyzedInstruction);
1204                return;
1205            case APUT:
1206                verify32BitPrimitiveAput(analyzedInstruction, RegisterType.Category.Integer);
1207                return;
1208            case APUT_BOOLEAN:
1209                verify32BitPrimitiveAput(analyzedInstruction, RegisterType.Category.Boolean);
1210                return;
1211            case APUT_BYTE:
1212                verify32BitPrimitiveAput(analyzedInstruction, RegisterType.Category.Byte);
1213                return;
1214            case APUT_CHAR:
1215                verify32BitPrimitiveAput(analyzedInstruction, RegisterType.Category.Char);
1216                return;
1217            case APUT_SHORT:
1218                verify32BitPrimitiveAput(analyzedInstruction, RegisterType.Category.Short);
1219                return;
1220            case APUT_WIDE:
1221                verifyAputWide(analyzedInstruction);
1222                return;
1223            case APUT_OBJECT:
1224                verifyAputObject(analyzedInstruction);
1225                return;
1226            case IGET:
1227                verify32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Integer);
1228                return;
1229            case IGET_BOOLEAN:
1230                verify32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Boolean);
1231                return;
1232            case IGET_BYTE:
1233                verify32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Byte);
1234                return;
1235            case IGET_CHAR:
1236                verify32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Char);
1237                return;
1238            case IGET_SHORT:
1239                verify32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Short);
1240                return;
1241            case IGET_WIDE:
1242                verifyIgetWide(analyzedInstruction);
1243                return;
1244            case IGET_OBJECT:
1245                verifyIgetObject(analyzedInstruction);
1246                return;
1247            case IPUT:
1248                verify32BitPrimitiveIput(analyzedInstruction, RegisterType.Category.Integer);
1249                return;
1250            case IPUT_BOOLEAN:
1251                verify32BitPrimitiveIput(analyzedInstruction, RegisterType.Category.Boolean);
1252                return;
1253            case IPUT_BYTE:
1254                verify32BitPrimitiveIput(analyzedInstruction, RegisterType.Category.Byte);
1255                return;
1256            case IPUT_CHAR:
1257                verify32BitPrimitiveIput(analyzedInstruction, RegisterType.Category.Char);
1258                return;
1259            case IPUT_SHORT:
1260                verify32BitPrimitiveIput(analyzedInstruction, RegisterType.Category.Short);
1261                return;
1262            case IPUT_WIDE:
1263                verifyIputWide(analyzedInstruction);
1264                return;
1265            case IPUT_OBJECT:
1266                verifyIputObject(analyzedInstruction);
1267                return;
1268            case SGET:
1269                verify32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Integer);
1270                return;
1271            case SGET_BOOLEAN:
1272                verify32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Boolean);
1273                return;
1274            case SGET_BYTE:
1275                verify32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Byte);
1276                return;
1277            case SGET_CHAR:
1278                verify32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Char);
1279                return;
1280            case SGET_SHORT:
1281                verify32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Short);
1282                return;
1283            case SGET_WIDE:
1284                verifySgetWide(analyzedInstruction);
1285                return;
1286            case SGET_OBJECT:
1287                verifySgetObject(analyzedInstruction);
1288                return;
1289            case SPUT:
1290                verify32BitPrimitiveSput(analyzedInstruction, RegisterType.Category.Integer);
1291                return;
1292            case SPUT_BOOLEAN:
1293                verify32BitPrimitiveSput(analyzedInstruction, RegisterType.Category.Boolean);
1294                return;
1295            case SPUT_BYTE:
1296                verify32BitPrimitiveSput(analyzedInstruction, RegisterType.Category.Byte);
1297                return;
1298            case SPUT_CHAR:
1299                verify32BitPrimitiveSput(analyzedInstruction, RegisterType.Category.Char);
1300                return;
1301            case SPUT_SHORT:
1302                verify32BitPrimitiveSput(analyzedInstruction, RegisterType.Category.Short);
1303                return;
1304            case SPUT_WIDE:
1305                verifySputWide(analyzedInstruction);
1306                return;
1307            case SPUT_OBJECT:
1308                verifySputObject(analyzedInstruction);
1309                return;
1310            case INVOKE_VIRTUAL:
1311                verifyInvoke(analyzedInstruction, INVOKE_VIRTUAL);
1312                return;
1313            case INVOKE_SUPER:
1314                verifyInvoke(analyzedInstruction, INVOKE_SUPER);
1315                return;
1316            case INVOKE_DIRECT:
1317                verifyInvoke(analyzedInstruction, INVOKE_DIRECT);
1318                return;
1319            case INVOKE_STATIC:
1320                verifyInvoke(analyzedInstruction, INVOKE_STATIC);
1321                return;
1322            case INVOKE_INTERFACE:
1323                verifyInvoke(analyzedInstruction, INVOKE_INTERFACE);
1324                return;
1325            case INVOKE_VIRTUAL_RANGE:
1326                verifyInvokeRange(analyzedInstruction, INVOKE_VIRTUAL);
1327                return;
1328            case INVOKE_SUPER_RANGE:
1329                verifyInvokeRange(analyzedInstruction, INVOKE_SUPER);
1330                return;
1331            case INVOKE_DIRECT_RANGE:
1332                verifyInvokeRange(analyzedInstruction, INVOKE_DIRECT);
1333                return;
1334            case INVOKE_STATIC_RANGE:
1335                verifyInvokeRange(analyzedInstruction, INVOKE_STATIC);
1336                return;
1337            case INVOKE_INTERFACE_RANGE:
1338                verifyInvokeRange(analyzedInstruction, INVOKE_INTERFACE);
1339                return;
1340            case NEG_INT:
1341            case NOT_INT:
1342                verifyUnaryOp(analyzedInstruction, Primitive32BitCategories);
1343                return;
1344            case NEG_LONG:
1345            case NOT_LONG:
1346                verifyUnaryOp(analyzedInstruction, WideLowCategories);
1347                return;
1348            case NEG_FLOAT:
1349                verifyUnaryOp(analyzedInstruction, Primitive32BitCategories);
1350                return;
1351            case NEG_DOUBLE:
1352                verifyUnaryOp(analyzedInstruction, WideLowCategories);
1353                return;
1354            case INT_TO_LONG:
1355                verifyUnaryOp(analyzedInstruction, Primitive32BitCategories);
1356                return;
1357            case INT_TO_FLOAT:
1358                verifyUnaryOp(analyzedInstruction, Primitive32BitCategories);
1359                return;
1360            case INT_TO_DOUBLE:
1361                verifyUnaryOp(analyzedInstruction, Primitive32BitCategories);
1362                return;
1363            case LONG_TO_INT:
1364            case DOUBLE_TO_INT:
1365                verifyUnaryOp(analyzedInstruction, WideLowCategories);
1366                return;
1367            case LONG_TO_FLOAT:
1368            case DOUBLE_TO_FLOAT:
1369                verifyUnaryOp(analyzedInstruction, WideLowCategories);
1370                return;
1371            case LONG_TO_DOUBLE:
1372                verifyUnaryOp(analyzedInstruction, WideLowCategories);
1373                return;
1374            case FLOAT_TO_INT:
1375                verifyUnaryOp(analyzedInstruction, Primitive32BitCategories);
1376                return;
1377            case FLOAT_TO_LONG:
1378                verifyUnaryOp(analyzedInstruction, Primitive32BitCategories);
1379                return;
1380            case FLOAT_TO_DOUBLE:
1381                verifyUnaryOp(analyzedInstruction, Primitive32BitCategories);
1382                return;
1383            case DOUBLE_TO_LONG:
1384                verifyUnaryOp(analyzedInstruction, WideLowCategories);
1385                return;
1386            case INT_TO_BYTE:
1387                verifyUnaryOp(analyzedInstruction, Primitive32BitCategories);
1388                return;
1389            case INT_TO_CHAR:
1390                verifyUnaryOp(analyzedInstruction, Primitive32BitCategories);
1391                return;
1392            case INT_TO_SHORT:
1393                verifyUnaryOp(analyzedInstruction, Primitive32BitCategories);
1394                return;
1395            case ADD_INT:
1396            case SUB_INT:
1397            case MUL_INT:
1398            case DIV_INT:
1399            case REM_INT:
1400            case SHL_INT:
1401            case SHR_INT:
1402            case USHR_INT:
1403            case AND_INT:
1404            case OR_INT:
1405            case XOR_INT:
1406                verifyBinaryOp(analyzedInstruction, Primitive32BitCategories, Primitive32BitCategories);
1407                return;
1408            case ADD_LONG:
1409            case SUB_LONG:
1410            case MUL_LONG:
1411            case DIV_LONG:
1412            case REM_LONG:
1413            case AND_LONG:
1414            case OR_LONG:
1415            case XOR_LONG:
1416                verifyBinaryOp(analyzedInstruction, WideLowCategories, WideLowCategories);
1417                return;
1418            case SHL_LONG:
1419            case SHR_LONG:
1420            case USHR_LONG:
1421                verifyBinaryOp(analyzedInstruction, WideLowCategories, Primitive32BitCategories);
1422                return;
1423            case ADD_FLOAT:
1424            case SUB_FLOAT:
1425            case MUL_FLOAT:
1426            case DIV_FLOAT:
1427            case REM_FLOAT:
1428                verifyBinaryOp(analyzedInstruction, Primitive32BitCategories, Primitive32BitCategories);
1429                return;
1430            case ADD_DOUBLE:
1431            case SUB_DOUBLE:
1432            case MUL_DOUBLE:
1433            case DIV_DOUBLE:
1434            case REM_DOUBLE:
1435                verifyBinaryOp(analyzedInstruction, WideLowCategories, WideLowCategories);
1436                return;
1437            case ADD_INT_2ADDR:
1438            case SUB_INT_2ADDR:
1439            case MUL_INT_2ADDR:
1440            case DIV_INT_2ADDR:
1441            case REM_INT_2ADDR:
1442            case SHL_INT_2ADDR:
1443            case SHR_INT_2ADDR:
1444            case USHR_INT_2ADDR:
1445            case AND_INT_2ADDR:
1446            case OR_INT_2ADDR:
1447            case XOR_INT_2ADDR:
1448                verifyBinary2AddrOp(analyzedInstruction, Primitive32BitCategories, Primitive32BitCategories);
1449                return;
1450            case ADD_LONG_2ADDR:
1451            case SUB_LONG_2ADDR:
1452            case MUL_LONG_2ADDR:
1453            case DIV_LONG_2ADDR:
1454            case REM_LONG_2ADDR:
1455            case AND_LONG_2ADDR:
1456            case OR_LONG_2ADDR:
1457            case XOR_LONG_2ADDR:
1458                verifyBinary2AddrOp(analyzedInstruction, WideLowCategories, WideLowCategories);
1459                return;
1460            case SHL_LONG_2ADDR:
1461            case SHR_LONG_2ADDR:
1462            case USHR_LONG_2ADDR:
1463                verifyBinary2AddrOp(analyzedInstruction, WideLowCategories, Primitive32BitCategories);
1464                return;
1465            case ADD_FLOAT_2ADDR:
1466            case SUB_FLOAT_2ADDR:
1467            case MUL_FLOAT_2ADDR:
1468            case DIV_FLOAT_2ADDR:
1469            case REM_FLOAT_2ADDR:
1470                verifyBinary2AddrOp(analyzedInstruction, Primitive32BitCategories, Primitive32BitCategories);
1471                return;
1472            case ADD_DOUBLE_2ADDR:
1473            case SUB_DOUBLE_2ADDR:
1474            case MUL_DOUBLE_2ADDR:
1475            case DIV_DOUBLE_2ADDR:
1476            case REM_DOUBLE_2ADDR:
1477                verifyBinary2AddrOp(analyzedInstruction, WideLowCategories, WideLowCategories);
1478                return;
1479            case ADD_INT_LIT16:
1480            case RSUB_INT:
1481            case MUL_INT_LIT16:
1482            case DIV_INT_LIT16:
1483            case REM_INT_LIT16:
1484                verifyLiteralBinaryOp(analyzedInstruction);
1485                return;
1486            case AND_INT_LIT16:
1487            case OR_INT_LIT16:
1488            case XOR_INT_LIT16:
1489                verifyLiteralBinaryOp(analyzedInstruction);
1490                return;
1491            case ADD_INT_LIT8:
1492            case RSUB_INT_LIT8:
1493            case MUL_INT_LIT8:
1494            case DIV_INT_LIT8:
1495            case REM_INT_LIT8:
1496            case SHL_INT_LIT8:
1497                verifyLiteralBinaryOp(analyzedInstruction);
1498                return;
1499            case AND_INT_LIT8:
1500            case OR_INT_LIT8:
1501            case XOR_INT_LIT8:
1502                verifyLiteralBinaryOp(analyzedInstruction);
1503                return;
1504            case SHR_INT_LIT8:
1505                verifyLiteralBinaryOp(analyzedInstruction);
1506                return;
1507            case USHR_INT_LIT8:
1508                verifyLiteralBinaryOp(analyzedInstruction);
1509                return;
1510            case IGET_VOLATILE:
1511            case IPUT_VOLATILE:
1512            case SGET_VOLATILE:
1513            case SPUT_VOLATILE:
1514            case IGET_OBJECT_VOLATILE:
1515            case IGET_WIDE_VOLATILE:
1516            case IPUT_WIDE_VOLATILE:
1517            case SGET_WIDE_VOLATILE:
1518            case SPUT_WIDE_VOLATILE:
1519            case THROW_VERIFICATION_ERROR:
1520            case EXECUTE_INLINE:
1521            case EXECUTE_INLINE_RANGE:
1522            case INVOKE_DIRECT_EMPTY:
1523            case IGET_QUICK:
1524            case IGET_WIDE_QUICK:
1525            case IGET_OBJECT_QUICK:
1526            case IPUT_QUICK:
1527            case IPUT_WIDE_QUICK:
1528            case IPUT_OBJECT_QUICK:
1529            case INVOKE_VIRTUAL_QUICK:
1530            case INVOKE_SUPER_QUICK:
1531            case INVOKE_VIRTUAL_QUICK_RANGE:
1532            case INVOKE_SUPER_QUICK_RANGE:
1533            case IPUT_OBJECT_VOLATILE:
1534            case SGET_OBJECT_VOLATILE:
1535            case SPUT_OBJECT_VOLATILE:
1536                //TODO: throw validation exception?
1537            default:
1538                assert false;
1539                return;
1540        }
1541    }
1542
1543    private static final EnumSet<RegisterType.Category> Primitive32BitCategories = EnumSet.of(
1544            RegisterType.Category.Null,
1545            RegisterType.Category.One,
1546            RegisterType.Category.Boolean,
1547            RegisterType.Category.Byte,
1548            RegisterType.Category.PosByte,
1549            RegisterType.Category.Short,
1550            RegisterType.Category.PosShort,
1551            RegisterType.Category.Char,
1552            RegisterType.Category.Integer,
1553            RegisterType.Category.Float);
1554
1555    private static final EnumSet<RegisterType.Category> WideLowCategories = EnumSet.of(
1556            RegisterType.Category.LongLo,
1557            RegisterType.Category.DoubleLo);
1558
1559    private static final EnumSet<RegisterType.Category> WideHighCategories = EnumSet.of(
1560            RegisterType.Category.LongHi,
1561            RegisterType.Category.DoubleHi);
1562
1563    private static final EnumSet<RegisterType.Category> ReferenceCategories = EnumSet.of(
1564            RegisterType.Category.Null,
1565            RegisterType.Category.Reference);
1566
1567    private static final EnumSet<RegisterType.Category> ReferenceOrUninitThisCategories = EnumSet.of(
1568            RegisterType.Category.Null,
1569            RegisterType.Category.UninitThis,
1570            RegisterType.Category.Reference);
1571
1572    private static final EnumSet<RegisterType.Category> ReferenceOrUninitCategories = EnumSet.of(
1573            RegisterType.Category.Null,
1574            RegisterType.Category.UninitRef,
1575            RegisterType.Category.UninitThis,
1576            RegisterType.Category.Reference);
1577
1578    private static final EnumSet<RegisterType.Category> ReferenceAndPrimitive32BitCategories = EnumSet.of(
1579            RegisterType.Category.Null,
1580            RegisterType.Category.One,
1581            RegisterType.Category.Boolean,
1582            RegisterType.Category.Byte,
1583            RegisterType.Category.PosByte,
1584            RegisterType.Category.Short,
1585            RegisterType.Category.PosShort,
1586            RegisterType.Category.Char,
1587            RegisterType.Category.Integer,
1588            RegisterType.Category.Float,
1589            RegisterType.Category.Reference);
1590
1591    private static final EnumSet<RegisterType.Category> BooleanCategories = EnumSet.of(
1592            RegisterType.Category.Null,
1593            RegisterType.Category.One,
1594            RegisterType.Category.Boolean);
1595
1596    private void analyzeMove(AnalyzedInstruction analyzedInstruction) {
1597        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
1598
1599        RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1600        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, sourceRegisterType);
1601    }
1602
1603    private void verifyMove(AnalyzedInstruction analyzedInstruction, EnumSet validCategories) {
1604        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
1605
1606        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), validCategories);
1607    }
1608
1609    private void analyzeMoveResult(AnalyzedInstruction analyzedInstruction) {
1610        AnalyzedInstruction previousInstruction = instructions.valueAt(analyzedInstruction.instructionIndex-1);
1611        if (!previousInstruction.instruction.opcode.setsResult()) {
1612            throw new ValidationException(analyzedInstruction.instruction.opcode.name + " must occur after an " +
1613                    "invoke-*/fill-new-array instruction");
1614        }
1615
1616        RegisterType resultRegisterType;
1617        InstructionWithReference invokeInstruction = (InstructionWithReference)previousInstruction.instruction;
1618        Item item = invokeInstruction.getReferencedItem();
1619
1620        if (item.getItemType() == ItemType.TYPE_METHOD_ID_ITEM) {
1621            resultRegisterType = RegisterType.getRegisterTypeForTypeIdItem(
1622                    ((MethodIdItem)item).getPrototype().getReturnType());
1623        } else {
1624            assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
1625            resultRegisterType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item);
1626        }
1627
1628        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, resultRegisterType);
1629    }
1630
1631    private void verifyMoveResult(AnalyzedInstruction analyzedInstruction,
1632                                     EnumSet<RegisterType.Category> allowedCategories) {
1633        if (analyzedInstruction.instructionIndex == 0) {
1634            throw new ValidationException(analyzedInstruction.instruction.opcode.name + " cannot be the first " +
1635                    "instruction in a method. It must occur after an invoke-*/fill-new-array instruction");
1636        }
1637
1638        AnalyzedInstruction previousInstruction = instructions.valueAt(analyzedInstruction.instructionIndex-1);
1639
1640        if (!previousInstruction.instruction.opcode.setsResult()) {
1641            throw new ValidationException(analyzedInstruction.instruction.opcode.name + " must occur after an " +
1642                    "invoke-*/fill-new-array instruction");
1643        }
1644
1645        //TODO: does dalvik allow a move-result after an invoke with a void return type?
1646        RegisterType resultRegisterType;
1647
1648        InstructionWithReference invokeInstruction = (InstructionWithReference)previousInstruction.getInstruction();
1649        Item item = invokeInstruction.getReferencedItem();
1650
1651        if (item instanceof MethodIdItem) {
1652            resultRegisterType = RegisterType.getRegisterTypeForTypeIdItem(
1653                    ((MethodIdItem)item).getPrototype().getReturnType());
1654        } else {
1655            assert item instanceof TypeIdItem;
1656            resultRegisterType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item);
1657        }
1658
1659        if (!allowedCategories.contains(resultRegisterType.category)) {
1660            throw new ValidationException(String.format("Wrong move-result* instruction for return value %s",
1661                    resultRegisterType.toString()));
1662        }
1663    }
1664
1665    private void analyzeMoveException(AnalyzedInstruction analyzedInstruction) {
1666        CodeItem.TryItem[] tries = encodedMethod.codeItem.getTries();
1667        int instructionAddress = getInstructionAddress(analyzedInstruction);
1668
1669        if (tries == null) {
1670            throw new ValidationException("move-exception must be the first instruction in an exception handler block");
1671        }
1672
1673        RegisterType exceptionType = null;
1674
1675        for (CodeItem.TryItem tryItem: encodedMethod.codeItem.getTries()) {
1676            if (tryItem.encodedCatchHandler.getCatchAllHandlerAddress() == instructionAddress) {
1677                exceptionType = RegisterType.getRegisterType(RegisterType.Category.Reference,
1678                        ClassPath.getClassDef("Ljava/lang/Throwable;"));
1679                break;
1680            }
1681            for (CodeItem.EncodedTypeAddrPair handler: tryItem.encodedCatchHandler.handlers) {
1682                if (handler.getHandlerAddress() == instructionAddress) {
1683                    exceptionType = RegisterType.getRegisterTypeForTypeIdItem(handler.exceptionType)
1684                            .merge(exceptionType);
1685                }
1686            }
1687        }
1688
1689        if (exceptionType == null) {
1690            throw new ValidationException("move-exception must be the first instruction in an exception handler block");
1691        }
1692
1693        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, exceptionType);
1694    }
1695
1696    private void verifyMoveException(AnalyzedInstruction analyzedInstruction) {
1697        CodeItem.TryItem[] tries = encodedMethod.codeItem.getTries();
1698        int instructionAddress = getInstructionAddress(analyzedInstruction);
1699
1700        if (tries == null) {
1701            throw new ValidationException("move-exception must be the first instruction in an exception handler block");
1702        }
1703
1704        RegisterType exceptionType = null;
1705
1706        for (CodeItem.TryItem tryItem: encodedMethod.codeItem.getTries()) {
1707            if (tryItem.encodedCatchHandler.getCatchAllHandlerAddress() == instructionAddress) {
1708                exceptionType = RegisterType.getRegisterType(RegisterType.Category.Reference,
1709                        ClassPath.getClassDef("Ljava/lang/Throwable;"));
1710                break;
1711            }
1712            for (CodeItem.EncodedTypeAddrPair handler: tryItem.encodedCatchHandler.handlers) {
1713                if (handler.getHandlerAddress() == instructionAddress) {
1714                    exceptionType = RegisterType.getRegisterTypeForTypeIdItem(handler.exceptionType)
1715                            .merge(exceptionType);
1716                }
1717            }
1718        }
1719
1720        if (exceptionType == null) {
1721            throw new ValidationException("move-exception must be the first instruction in an exception handler block");
1722        }
1723
1724        //TODO: check if the type is a throwable. Should we throw a ValidationException or print a warning? (does dalvik validate that it's a throwable? It doesn't in CodeVerify.c, but it might check in DexSwapVerify.c)
1725        if (exceptionType.category != RegisterType.Category.Reference) {
1726            throw new ValidationException(String.format("Exception type %s is not a reference type",
1727                    exceptionType.toString()));
1728        }
1729    }
1730
1731    private void verifyReturnVoid(AnalyzedInstruction analyzedInstruction) {
1732        TypeIdItem returnType = encodedMethod.method.getPrototype().getReturnType();
1733        if (returnType.getTypeDescriptor().charAt(0) != 'V') {
1734            //TODO: could add which return-* variation should be used instead
1735            throw new ValidationException("Cannot use return-void with a non-void return type (" +
1736                returnType.getTypeDescriptor() + ")");
1737        }
1738    }
1739
1740    private void verifyReturn(AnalyzedInstruction analyzedInstruction, EnumSet validCategories) {
1741        /*if (this.isInstanceConstructor()) {
1742            checkConstructorReturn(analyzedInstruction);
1743        }*/
1744
1745        SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
1746        int returnRegister = instruction.getRegisterA();
1747        RegisterType returnRegisterType = getAndCheckSourceRegister(analyzedInstruction, returnRegister,
1748                validCategories);
1749
1750        TypeIdItem returnType = encodedMethod.method.getPrototype().getReturnType();
1751        if (returnType.getTypeDescriptor().charAt(0) == 'V') {
1752            throw new ValidationException("Cannot use return with a void return type. Use return-void instead");
1753        }
1754
1755        RegisterType methodReturnRegisterType = RegisterType.getRegisterTypeForTypeIdItem(returnType);
1756
1757        if (!validCategories.contains(methodReturnRegisterType.category)) {
1758            //TODO: could add which return-* variation should be used instead
1759            throw new ValidationException(String.format("Cannot use %s with return type %s",
1760                    analyzedInstruction.instruction.opcode.name, returnType.getTypeDescriptor()));
1761        }
1762
1763        if (validCategories == ReferenceCategories) {
1764            if (methodReturnRegisterType.type.isInterface()) {
1765                if (returnRegisterType.category != RegisterType.Category.Null &&
1766                    !returnRegisterType.type.implementsInterface(methodReturnRegisterType.type)) {
1767                    //TODO: how to handle warnings?
1768                }
1769            } else {
1770                if (returnRegisterType.category == RegisterType.Category.Reference &&
1771                    !returnRegisterType.type.extendsClass(methodReturnRegisterType.type)) {
1772
1773                    throw new ValidationException(String.format("The return value in register v%d (%s) is not " +
1774                            "compatible with the method's return type %s", returnRegister,
1775                            returnRegisterType.type.getClassType(), methodReturnRegisterType.type.getClassType()));
1776                }
1777            }
1778        }
1779    }
1780
1781    private void analyzeConst(AnalyzedInstruction analyzedInstruction) {
1782        LiteralInstruction instruction = (LiteralInstruction)analyzedInstruction.instruction;
1783
1784        RegisterType newDestinationRegisterType = RegisterType.getRegisterTypeForLiteral(instruction.getLiteral());
1785
1786        //we assume that the literal value is a valid value for the given instruction type, because it's impossible
1787        //to store an invalid literal with the instruction. so we don't need to check the type of the literal
1788        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, newDestinationRegisterType);
1789    }
1790
1791    private void analyzeConstHigh16(AnalyzedInstruction analyzedInstruction) {
1792        //the literal value stored in the instruction is a 16-bit value. When shifted left by 16, it will always be an
1793        //integer
1794        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1795                RegisterType.getRegisterType(RegisterType.Category.Integer, null));
1796    }
1797
1798    private void analyzeWideConst(AnalyzedInstruction analyzedInstruction) {
1799        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1800                RegisterType.getRegisterType(RegisterType.Category.LongLo, null));
1801    }
1802
1803    private void analyzeConstString(AnalyzedInstruction analyzedInstruction) {
1804        ClassPath.ClassDef stringClassDef = ClassPath.getClassDef("Ljava/lang/String;");
1805        RegisterType stringType = RegisterType.getRegisterType(RegisterType.Category.Reference, stringClassDef);
1806        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, stringType);
1807    }
1808
1809    private void analyzeConstClass(AnalyzedInstruction analyzedInstruction) {
1810        ClassPath.ClassDef classClassDef = ClassPath.getClassDef("Ljava/lang/Class;");
1811        RegisterType classType = RegisterType.getRegisterType(RegisterType.Category.Reference, classClassDef);
1812
1813        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, classType);
1814    }
1815
1816
1817    private void verifyConstClass(AnalyzedInstruction analyzedInstruction) {
1818        ClassPath.ClassDef classClassDef = ClassPath.getClassDef("Ljava/lang/Class;");
1819        RegisterType classType = RegisterType.getRegisterType(RegisterType.Category.Reference, classClassDef);
1820
1821        InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
1822        Item item = instruction.getReferencedItem();
1823        assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
1824
1825        //TODO: need to check class access
1826        //make sure the referenced class is resolvable
1827        ClassPath.getClassDef((TypeIdItem)item);
1828    }
1829
1830    private void verifyMonitor(AnalyzedInstruction analyzedInstruction) {
1831        SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
1832        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), ReferenceCategories);
1833    }
1834
1835    private void analyzeCheckCast(AnalyzedInstruction analyzedInstruction) {
1836        InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
1837
1838        Item item = instruction.getReferencedItem();
1839        assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
1840
1841        RegisterType castRegisterType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item);
1842        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, castRegisterType);
1843    }
1844
1845    private void verifyCheckCast(AnalyzedInstruction analyzedInstruction) {
1846        {
1847            //ensure the "source" register is a reference type
1848            SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
1849
1850            RegisterType registerType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(),
1851                    ReferenceCategories);
1852        }
1853
1854        {
1855            //resolve and verify the class that we're casting to
1856            InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
1857
1858            Item item = instruction.getReferencedItem();
1859            assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
1860
1861            //TODO: need to check class access
1862            RegisterType castRegisterType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item);
1863            if (castRegisterType.category != RegisterType.Category.Reference) {
1864                //TODO: verify that dalvik allows a non-reference type..
1865                //TODO: print a warning, but don't re-throw the exception. dalvik allows a non-reference type during validation (but throws an exception at runtime)
1866            }
1867        }
1868    }
1869
1870    private void analyzeInstanceOf(AnalyzedInstruction analyzedInstruction) {
1871        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1872                RegisterType.getRegisterType(RegisterType.Category.Boolean, null));
1873    }
1874
1875    private void verifyInstanceOf(AnalyzedInstruction analyzedInstruction) {
1876        {
1877            //ensure the register that is being checks is a reference type
1878            TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
1879
1880            getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), ReferenceCategories);
1881        }
1882
1883        {
1884            //resolve and verify the class that we're checking against
1885            InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
1886
1887            Item item = instruction.getReferencedItem();
1888            assert  item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
1889            RegisterType registerType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item);
1890            if (registerType.category != RegisterType.Category.Reference) {
1891                throw new ValidationException(String.format("Cannot use instance-of with a non-reference type %s",
1892                        registerType.toString()));
1893            }
1894
1895            //TODO: is it valid to use an array type?
1896            //TODO: could probably do an even more sophisticated check, where we check the possible register types against the specified type. In some cases, we could determine that it always fails, and print a warning to that effect.
1897        }
1898    }
1899
1900    private void analyzeArrayLength(AnalyzedInstruction analyzedInstruction) {
1901        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1902                RegisterType.getRegisterType(RegisterType.Category.Integer, null));
1903    }
1904
1905    private void verifyArrayLength(AnalyzedInstruction analyzedInstruction) {
1906        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
1907
1908        int arrayRegisterNumber = instruction.getRegisterB();
1909        RegisterType arrayRegisterType = getAndCheckSourceRegister(analyzedInstruction, arrayRegisterNumber,
1910                ReferenceCategories);
1911
1912        if (arrayRegisterType.type != null) {
1913            if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
1914                throw new ValidationException(String.format("Cannot use array-length with non-array type %s",
1915                        arrayRegisterType.type.getClassType()));
1916            }
1917            assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
1918        }
1919    }
1920
1921    private void analyzeNewInstance(AnalyzedInstruction analyzedInstruction) {
1922        InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
1923
1924        int register = ((SingleRegisterInstruction)analyzedInstruction.instruction).getRegisterA();
1925        RegisterType destRegisterType = analyzedInstruction.getPostInstructionRegisterType(register);
1926        if (destRegisterType.category != RegisterType.Category.Unknown) {
1927            assert destRegisterType.category == RegisterType.Category.UninitRef;
1928
1929            //the post-instruction destination register will only be set if we have already analyzed this instruction
1930            //at least once. If this is the case, then the uninit reference has already been propagated to all
1931            //successors and nothing else needs to be done.
1932            return;
1933        }
1934
1935        Item item = instruction.getReferencedItem();
1936        assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
1937
1938        RegisterType classType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item);
1939
1940        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1941                RegisterType.getUnitializedReference(classType.type));
1942    }
1943
1944    private void verifyNewInstance(AnalyzedInstruction analyzedInstruction) {
1945        InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
1946
1947        int register = ((SingleRegisterInstruction)analyzedInstruction.instruction).getRegisterA();
1948        RegisterType destRegisterType = analyzedInstruction.postRegisterMap[register];
1949        if (destRegisterType.category != RegisterType.Category.Unknown) {
1950            assert destRegisterType.category == RegisterType.Category.UninitRef;
1951
1952            //the "post-instruction" destination register will only be set if we've gone over
1953            //this instruction at least once before. If this is the case, then we need to check
1954            //all the other registers, and make sure that none of them contain the same
1955            //uninitialized reference that is in the destination register.
1956
1957            for (int i=0; i<analyzedInstruction.postRegisterMap.length; i++) {
1958                if (i==register) {
1959                    continue;
1960                }
1961
1962                if (analyzedInstruction.getPreInstructionRegisterType(i) == destRegisterType) {
1963                    throw new ValidationException(String.format("Register v%d contains an uninitialized reference " +
1964                            "that was created by this new-instance instruction.", i));
1965                }
1966            }
1967
1968            return;
1969        }
1970
1971        Item item = instruction.getReferencedItem();
1972        assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
1973
1974        //TODO: need to check class access
1975        RegisterType classType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item);
1976        if (classType.category != RegisterType.Category.Reference) {
1977            throw new ValidationException(String.format("Cannot use new-instance with a non-reference type %s",
1978                    classType.toString()));
1979        }
1980
1981        if (((TypeIdItem)item).getTypeDescriptor().charAt(0) == '[') {
1982            throw new ValidationException("Cannot use array type \"" + ((TypeIdItem)item).getTypeDescriptor() +
1983                    "\" with new-instance. Use new-array instead.");
1984        }
1985    }
1986
1987    private void analyzeNewArray(AnalyzedInstruction analyzedInstruction) {
1988        InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
1989
1990        Item item = instruction.getReferencedItem();
1991        assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
1992
1993        RegisterType arrayType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item);
1994        assert arrayType.type instanceof ClassPath.ArrayClassDef;
1995
1996        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, arrayType);
1997    }
1998
1999    private void verifyNewArray(AnalyzedInstruction analyzedInstruction) {
2000        {
2001            TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
2002            getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), Primitive32BitCategories);
2003        }
2004
2005        InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
2006
2007        Item item = instruction.getReferencedItem();
2008        assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
2009
2010        RegisterType arrayType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item);
2011        assert arrayType.type instanceof ClassPath.ArrayClassDef;
2012
2013        if (arrayType.category != RegisterType.Category.Reference) {
2014            throw new ValidationException(String.format("Cannot use new-array with a non-reference type %s",
2015                    arrayType.toString()));
2016        }
2017        if (arrayType.type.getClassType().charAt(0) != '[') {
2018            throw new ValidationException("Cannot use non-array type \"" + arrayType.type.getClassType() +
2019                    "\" with new-array. Use new-instance instead.");
2020        }
2021    }
2022
2023    private void verifyFilledNewArrayCommon(AnalyzedInstruction analyzedInstruction,
2024                                               RegisterIterator registerIterator) {
2025        InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
2026
2027        RegisterType arrayType;
2028        RegisterType arrayImmediateElementType;
2029
2030        Item item = instruction.getReferencedItem();
2031        assert  item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
2032
2033        ClassPath.ClassDef classDef = ClassPath.getClassDef((TypeIdItem)item);
2034
2035        if (classDef.getClassType().charAt(0) != '[') {
2036            throw new ValidationException("Cannot use non-array type \"" + classDef.getClassType() +
2037                "\" with new-array. Use new-instance instead.");
2038        }
2039
2040        ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)classDef;
2041        arrayType = RegisterType.getRegisterType(RegisterType.Category.Reference, classDef);
2042        arrayImmediateElementType = RegisterType.getRegisterTypeForType(
2043                arrayClassDef.getImmediateElementClass().getClassType());
2044        String baseElementType = arrayClassDef.getBaseElementClass().getClassType();
2045        if (baseElementType.charAt(0) == 'J' || baseElementType.charAt(0) == 'D') {
2046            throw new ValidationException("Cannot use filled-new-array to create an array of wide values " +
2047                    "(long or double)");
2048        }
2049
2050        do {
2051            int register = registerIterator.getRegister();
2052            RegisterType elementType = analyzedInstruction.getPreInstructionRegisterType(register);
2053            assert elementType != null;
2054
2055            if (!elementType.canBeAssignedTo(arrayImmediateElementType)) {
2056                throw new ValidationException("Register v" + Integer.toString(register) + " is of type " +
2057                        elementType.toString() + " and is incompatible with the array type " +
2058                        arrayType.type.getClassType());
2059            }
2060        } while (registerIterator.moveNext());
2061    }
2062
2063    private void verifyFilledNewArray(AnalyzedInstruction analyzedInstruction) {
2064        FiveRegisterInstruction instruction = (FiveRegisterInstruction)analyzedInstruction.instruction;
2065        verifyFilledNewArrayCommon(analyzedInstruction, new Format35cRegisterIterator(instruction));
2066    }
2067
2068    private void verifyFilledNewArrayRange(AnalyzedInstruction analyzedInstruction) {
2069        RegisterRangeInstruction instruction = (RegisterRangeInstruction)analyzedInstruction.instruction;
2070
2071        //instruction.getStartRegister() and instruction.getRegCount() both return an int value, but are actually
2072        //unsigned 16 bit values, so we don't have to worry about overflowing an int when adding them together
2073        if (instruction.getStartRegister() + instruction.getRegCount() >= 1<<16) {
2074            throw new ValidationException(String.format("Invalid register range {v%d .. v%d}. The ending register " +
2075                    "is larger than the largest allowed register of v65535.",
2076                    instruction.getStartRegister(),
2077                    instruction.getStartRegister() + instruction.getRegCount() - 1));
2078        }
2079
2080        verifyFilledNewArrayCommon(analyzedInstruction, new Format3rcRegisterIterator(instruction));
2081    }
2082
2083    private void verifyFillArrayData(AnalyzedInstruction analyzedInstruction) {
2084        SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
2085
2086        int register = instruction.getRegisterA();
2087        RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(register);
2088        assert registerType != null;
2089
2090        if (registerType.category == RegisterType.Category.Null) {
2091            return;
2092        }
2093
2094        if (registerType.category != RegisterType.Category.Reference) {
2095            throw new ValidationException(String.format("Cannot use fill-array-data with non-array register v%d of " +
2096                    "type %s", register, registerType.toString()));
2097        }
2098
2099        assert registerType.type instanceof ClassPath.ArrayClassDef;
2100        ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)registerType.type;
2101
2102        if (arrayClassDef.getArrayDimensions() != 1) {
2103            throw new ValidationException(String.format("Cannot use fill-array-data with array type %s. It can only " +
2104                    "be used with a one-dimensional array of primitives.", arrayClassDef.getClassType()));
2105        }
2106
2107        int elementWidth;
2108        switch (arrayClassDef.getBaseElementClass().getClassType().charAt(0)) {
2109            case 'Z':
2110            case 'B':
2111                elementWidth = 1;
2112                break;
2113            case 'C':
2114            case 'S':
2115                elementWidth = 2;
2116                break;
2117            case 'I':
2118            case 'F':
2119                elementWidth = 4;
2120                break;
2121            case 'J':
2122            case 'D':
2123                elementWidth = 8;
2124                break;
2125            default:
2126                throw new ValidationException(String.format("Cannot use fill-array-data with array type %s. It can " +
2127                        "only be used with a one-dimensional array of primitives.", arrayClassDef.getClassType()));
2128        }
2129
2130
2131        int arrayDataAddressOffset = ((OffsetInstruction)analyzedInstruction.instruction).getTargetAddressOffset();
2132        int arrayDataCodeAddress = getInstructionAddress(analyzedInstruction) + arrayDataAddressOffset;
2133        AnalyzedInstruction arrayDataInstruction = this.instructions.get(arrayDataCodeAddress);
2134        if (arrayDataInstruction == null || arrayDataInstruction.instruction.getFormat() != Format.ArrayData) {
2135            throw new ValidationException(String.format("Could not find an array data structure at code address 0x%x",
2136                    arrayDataCodeAddress));
2137        }
2138
2139        ArrayDataPseudoInstruction arrayDataPseudoInstruction =
2140                (ArrayDataPseudoInstruction)arrayDataInstruction.instruction;
2141
2142        if (elementWidth != arrayDataPseudoInstruction.getElementWidth()) {
2143            throw new ValidationException(String.format("The array data at code address 0x%x does not have the " +
2144                    "correct element width for array type %s. Expecting element width %d, got element width %d.",
2145                    arrayDataCodeAddress, arrayClassDef.getClassType(), elementWidth,
2146                    arrayDataPseudoInstruction.getElementWidth()));
2147        }
2148    }
2149
2150    private void verifyThrow(AnalyzedInstruction analyzedInstruction) {
2151        int register = ((SingleRegisterInstruction)analyzedInstruction.instruction).getRegisterA();
2152
2153        RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(register);
2154        assert registerType != null;
2155
2156        if (registerType.category == RegisterType.Category.Null) {
2157            return;
2158        }
2159
2160        if (registerType.category != RegisterType.Category.Reference) {
2161            throw new ValidationException(String.format("Cannot use throw with non-reference type %s in register v%d",
2162                    registerType.toString(), register));
2163        }
2164
2165        assert registerType.type != null;
2166
2167        if (!registerType.type.extendsClass(ClassPath.getClassDef("Ljava/lang/Throwable;"))) {
2168            throw new ValidationException(String.format("Cannot use throw with non-throwable type %s in register v%d",
2169                    registerType.type.getClassType(), register));
2170        }
2171    }
2172
2173    private void analyzeArrayDataOrSwitch(AnalyzedInstruction analyzedInstruction) {
2174        int dataAddressOffset = ((OffsetInstruction)analyzedInstruction.instruction).getTargetAddressOffset();
2175
2176        int dataCodeAddress = this.getInstructionAddress(analyzedInstruction) + dataAddressOffset;
2177        AnalyzedInstruction dataAnalyzedInstruction = instructions.get(dataCodeAddress);
2178
2179        if (dataAnalyzedInstruction != null) {
2180            dataAnalyzedInstruction.dead = false;
2181
2182            //if there is a preceding nop, it's deadness should be the same
2183            AnalyzedInstruction priorInstruction =
2184                    instructions.valueAt(dataAnalyzedInstruction.getInstructionIndex()-1);
2185            if (priorInstruction.getInstruction().opcode == Opcode.NOP &&
2186                    !priorInstruction.getInstruction().getFormat().variableSizeFormat) {
2187
2188                priorInstruction.dead = false;
2189            }
2190        }
2191    }
2192
2193    private void verifySwitch(AnalyzedInstruction analyzedInstruction, Format expectedSwitchDataFormat) {
2194        int register = ((SingleRegisterInstruction)analyzedInstruction.instruction).getRegisterA();
2195        int switchCodeAddressOffset = ((OffsetInstruction)analyzedInstruction.instruction).getTargetAddressOffset();
2196
2197        getAndCheckSourceRegister(analyzedInstruction, register, Primitive32BitCategories);
2198
2199        int switchDataCodeAddress = this.getInstructionAddress(analyzedInstruction) + switchCodeAddressOffset;
2200        AnalyzedInstruction switchDataAnalyzedInstruction = instructions.get(switchDataCodeAddress);
2201
2202        if (switchDataAnalyzedInstruction == null ||
2203            switchDataAnalyzedInstruction.instruction.getFormat() != expectedSwitchDataFormat) {
2204            throw new ValidationException(String.format("There is no %s structure at code address 0x%x",
2205                    expectedSwitchDataFormat.name(), switchDataCodeAddress));
2206        }
2207    }
2208
2209    private void analyzeFloatWideCmp(AnalyzedInstruction analyzedInstruction) {
2210        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
2211                RegisterType.getRegisterType(RegisterType.Category.Byte, null));
2212    }
2213
2214    private void verifyFloatWideCmp(AnalyzedInstruction analyzedInstruction, EnumSet validCategories) {
2215        ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
2216
2217        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), validCategories);
2218        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), validCategories);
2219    }
2220
2221    private void verifyIfEqNe(AnalyzedInstruction analyzedInstruction) {
2222        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
2223
2224        RegisterType registerType1 = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA());
2225        assert registerType1 != null;
2226
2227        RegisterType registerType2 = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
2228        assert registerType2 != null;
2229
2230        if (!(
2231                (ReferenceCategories.contains(registerType1.category) &&
2232                ReferenceCategories.contains(registerType2.category))
2233                    ||
2234                (Primitive32BitCategories.contains(registerType1.category) &&
2235                Primitive32BitCategories.contains(registerType2.category))
2236              )) {
2237
2238            throw new ValidationException(String.format("%s cannot be used on registers of dissimilar types %s and " +
2239                    "%s. They must both be a reference type or a primitive 32 bit type.",
2240                    analyzedInstruction.instruction.opcode.name, registerType1.toString(), registerType2.toString()));
2241        }
2242    }
2243
2244    private void verifyIf(AnalyzedInstruction analyzedInstruction) {
2245        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
2246
2247        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), Primitive32BitCategories);
2248        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), Primitive32BitCategories);
2249    }
2250
2251    private void verifyIfEqzNez(AnalyzedInstruction analyzedInstruction) {
2252        SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
2253
2254        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(),
2255                ReferenceAndPrimitive32BitCategories);
2256    }
2257
2258    private void verifyIfz(AnalyzedInstruction analyzedInstruction) {
2259        SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
2260
2261        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), Primitive32BitCategories);
2262    }
2263
2264    private void analyze32BitPrimitiveAget(AnalyzedInstruction analyzedInstruction,
2265                                             RegisterType.Category instructionCategory) {
2266        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
2267                RegisterType.getRegisterType(instructionCategory, null));
2268    }
2269
2270    private void verify32BitPrimitiveAget(AnalyzedInstruction analyzedInstruction,
2271                                             RegisterType.Category instructionCategory) {
2272        ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
2273
2274        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories);
2275
2276        RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
2277        assert arrayRegisterType != null;
2278
2279        if (arrayRegisterType.category != RegisterType.Category.Null) {
2280            if (arrayRegisterType.category != RegisterType.Category.Reference) {
2281                throw new ValidationException(String.format("Cannot use %s with non-array type %s",
2282                        analyzedInstruction.instruction.opcode.name, arrayRegisterType.category.toString()));
2283            }
2284
2285            assert arrayRegisterType.type != null;
2286            if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
2287                throw new ValidationException(String.format("Cannot use %s with non-array type %s",
2288                        analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType()));
2289            }
2290
2291            assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
2292            ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type;
2293
2294            if (arrayClassDef.getArrayDimensions() != 1) {
2295                throw new ValidationException(String.format("Cannot use %s with multi-dimensional array type %s",
2296                        analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType()));
2297            }
2298
2299            RegisterType arrayBaseType =
2300                    RegisterType.getRegisterTypeForType(arrayClassDef.getBaseElementClass().getClassType());
2301            if (!checkArrayFieldAssignment(arrayBaseType.category, instructionCategory)) {
2302                throw new ValidationException(String.format("Cannot use %s with array type %s. Incorrect array type " +
2303                        "for the instruction.", analyzedInstruction.instruction.opcode.name,
2304                        arrayRegisterType.type.getClassType()));
2305            }
2306        }
2307    }
2308
2309    private void analyzeAgetWide(AnalyzedInstruction analyzedInstruction) {
2310        ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
2311
2312        RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
2313        assert arrayRegisterType != null;
2314
2315        if (arrayRegisterType.category != RegisterType.Category.Null) {
2316            assert arrayRegisterType.type != null;
2317            if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
2318                throw new ValidationException(String.format("Cannot use aget-wide with non-array type %s",
2319                        arrayRegisterType.type.getClassType()));
2320            }
2321
2322            assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
2323            ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type;
2324
2325            char arrayBaseType = arrayClassDef.getBaseElementClass().getClassType().charAt(0);
2326            if (arrayBaseType == 'J') {
2327                setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
2328                        RegisterType.getRegisterType(RegisterType.Category.LongLo, null));
2329            } else if (arrayBaseType == 'D') {
2330                setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
2331                        RegisterType.getRegisterType(RegisterType.Category.DoubleLo, null));
2332            } else {
2333                throw new ValidationException(String.format("Cannot use aget-wide with array type %s. Incorrect " +
2334                        "array type for the instruction.", arrayRegisterType.type.getClassType()));
2335            }
2336        } else {
2337            setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
2338                        RegisterType.getRegisterType(RegisterType.Category.LongLo, null));
2339        }
2340    }
2341
2342    private void verifyAgetWide(AnalyzedInstruction analyzedInstruction) {
2343        ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
2344
2345        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories);
2346
2347        RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
2348        assert arrayRegisterType != null;
2349
2350        if (arrayRegisterType.category != RegisterType.Category.Null) {
2351            if (arrayRegisterType.category != RegisterType.Category.Reference) {
2352                throw new ValidationException(String.format("Cannot use aget-wide with non-array type %s",
2353                        arrayRegisterType.category.toString()));
2354            }
2355
2356            assert arrayRegisterType.type != null;
2357            if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
2358                throw new ValidationException(String.format("Cannot use aget-wide with non-array type %s",
2359                        arrayRegisterType.type.getClassType()));
2360            }
2361
2362            assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
2363            ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type;
2364
2365            if (arrayClassDef.getArrayDimensions() != 1) {
2366                throw new ValidationException(String.format("Cannot use aget-wide with multi-dimensional array type %s",
2367                        arrayRegisterType.type.getClassType()));
2368            }
2369
2370            char arrayBaseType = arrayClassDef.getBaseElementClass().getClassType().charAt(0);
2371            if (arrayBaseType != 'J' && arrayBaseType != 'D') {
2372                throw new ValidationException(String.format("Cannot use aget-wide with array type %s. Incorrect " +
2373                        "array type for the instruction.", arrayRegisterType.type.getClassType()));
2374            }
2375        }
2376    }
2377
2378    private void analyzeAgetObject(AnalyzedInstruction analyzedInstruction) {
2379        ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
2380
2381        RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
2382        assert arrayRegisterType != null;
2383
2384        if (arrayRegisterType.category != RegisterType.Category.Null) {
2385            assert arrayRegisterType.type != null;
2386            if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
2387                throw new ValidationException(String.format("Cannot use aget-object with non-array type %s",
2388                        arrayRegisterType.type.getClassType()));
2389            }
2390
2391            assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
2392            ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type;
2393
2394            ClassPath.ClassDef elementClassDef = arrayClassDef.getImmediateElementClass();
2395            char elementTypePrefix = elementClassDef.getClassType().charAt(0);
2396            if (elementTypePrefix != 'L' && elementTypePrefix != '[') {
2397                throw new ValidationException(String.format("Cannot use aget-object with array type %s. Incorrect " +
2398                        "array type for the instruction.", arrayRegisterType.type.getClassType()));
2399            }
2400
2401            setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
2402                    RegisterType.getRegisterType(RegisterType.Category.Reference, elementClassDef));
2403        } else {
2404            setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
2405                    RegisterType.getRegisterType(RegisterType.Category.Null, null));
2406        }
2407    }
2408
2409    private void verifyAgetObject(AnalyzedInstruction analyzedInstruction) {
2410        ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
2411
2412        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories);
2413
2414        RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
2415        assert arrayRegisterType != null;
2416
2417        if (arrayRegisterType.category != RegisterType.Category.Null) {
2418            if (arrayRegisterType.category != RegisterType.Category.Reference) {
2419                throw new ValidationException(String.format("Cannot use aget-object with non-array type %s",
2420                        arrayRegisterType.category.toString()));
2421            }
2422
2423            assert arrayRegisterType.type != null;
2424            if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
2425                throw new ValidationException(String.format("Cannot use aget-object with non-array type %s",
2426                        arrayRegisterType.type.getClassType()));
2427            }
2428
2429            assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
2430            ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type;
2431
2432            ClassPath.ClassDef elementClassDef = arrayClassDef.getImmediateElementClass();
2433            char elementTypePrefix = elementClassDef.getClassType().charAt(0);
2434            if (elementTypePrefix != 'L' && elementTypePrefix != '[') {
2435                throw new ValidationException(String.format("Cannot use aget-object with array type %s. Incorrect " +
2436                        "array type for the instruction.", arrayRegisterType.type.getClassType()));
2437            }
2438        }
2439    }
2440
2441    private void verify32BitPrimitiveAput(AnalyzedInstruction analyzedInstruction,
2442                                             RegisterType.Category instructionCategory) {
2443        ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
2444
2445        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories);
2446
2447        RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA());
2448        assert sourceRegisterType != null;
2449        RegisterType instructionRegisterType = RegisterType.getRegisterType(instructionCategory, null);
2450        if (!sourceRegisterType.canBeAssignedTo(instructionRegisterType)) {
2451            throw new ValidationException(String.format("Cannot use %s with source register type %s.",
2452                    analyzedInstruction.instruction.opcode.name, sourceRegisterType.toString()));
2453        }
2454
2455
2456        RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
2457        assert arrayRegisterType != null;
2458
2459        if (arrayRegisterType.category != RegisterType.Category.Null) {
2460            if (arrayRegisterType.category != RegisterType.Category.Reference) {
2461                throw new ValidationException(String.format("Cannot use %s with non-array type %s",
2462                        analyzedInstruction.instruction.opcode.name, arrayRegisterType.category.toString()));
2463            }
2464
2465            assert arrayRegisterType.type != null;
2466            if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
2467                throw new ValidationException(String.format("Cannot use %s with non-array type %s",
2468                        analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType()));
2469            }
2470
2471            assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
2472            ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type;
2473
2474            if (arrayClassDef.getArrayDimensions() != 1) {
2475                throw new ValidationException(String.format("Cannot use %s with multi-dimensional array type %s",
2476                        analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType()));
2477            }
2478
2479            RegisterType arrayBaseType =
2480                    RegisterType.getRegisterTypeForType(arrayClassDef.getBaseElementClass().getClassType());
2481            if (!checkArrayFieldAssignment(arrayBaseType.category, instructionCategory)) {
2482                throw new ValidationException(String.format("Cannot use %s with array type %s. Incorrect array type " +
2483                        "for the instruction.", analyzedInstruction.instruction.opcode.name,
2484                        arrayRegisterType.type.getClassType()));
2485            }
2486        }
2487    }
2488
2489    private void verifyAputWide(AnalyzedInstruction analyzedInstruction) {
2490        ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
2491
2492        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories);
2493        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), WideLowCategories);
2494
2495        RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
2496        assert arrayRegisterType != null;
2497
2498        if (arrayRegisterType.category != RegisterType.Category.Null) {
2499            if (arrayRegisterType.category != RegisterType.Category.Reference) {
2500                throw new ValidationException(String.format("Cannot use aput-wide with non-array type %s",
2501                        arrayRegisterType.category.toString()));
2502            }
2503
2504            assert arrayRegisterType.type != null;
2505            if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
2506                throw new ValidationException(String.format("Cannot use aput-wide with non-array type %s",
2507                        arrayRegisterType.type.getClassType()));
2508            }
2509
2510            assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
2511            ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type;
2512
2513            if (arrayClassDef.getArrayDimensions() != 1) {
2514                throw new ValidationException(String.format("Cannot use aput-wide with multi-dimensional array type %s",
2515                        arrayRegisterType.type.getClassType()));
2516            }
2517
2518            char arrayBaseType = arrayClassDef.getBaseElementClass().getClassType().charAt(0);
2519            if (arrayBaseType != 'J' && arrayBaseType != 'D') {
2520                throw new ValidationException(String.format("Cannot use aput-wide with array type %s. Incorrect " +
2521                        "array type for the instruction.", arrayRegisterType.type.getClassType()));
2522            }
2523        }
2524    }
2525
2526    private void verifyAputObject(AnalyzedInstruction analyzedInstruction) {
2527        ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
2528
2529        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories);
2530
2531        RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA());
2532        assert sourceRegisterType != null;
2533
2534        //TODO: ensure sourceRegisterType is a Reference type?
2535
2536        RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
2537        assert arrayRegisterType != null;
2538
2539        if (arrayRegisterType.category != RegisterType.Category.Null) {
2540            //don't check the source type against the array type, just make sure it is an array of reference types
2541
2542            if (arrayRegisterType.category != RegisterType.Category.Reference) {
2543                throw new ValidationException(String.format("Cannot use aget-object with non-array type %s",
2544                        arrayRegisterType.category.toString()));
2545            }
2546
2547            assert arrayRegisterType.type != null;
2548            if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
2549                throw new ValidationException(String.format("Cannot use aget-object with non-array type %s",
2550                        arrayRegisterType.type.getClassType()));
2551            }
2552
2553            assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
2554            ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type;
2555
2556            ClassPath.ClassDef elementClassDef = arrayClassDef.getImmediateElementClass();
2557            char elementTypePrefix = elementClassDef.getClassType().charAt(0);
2558            if (elementTypePrefix != 'L' && elementTypePrefix != '[') {
2559                throw new ValidationException(String.format("Cannot use aget-object with array type %s. Incorrect " +
2560                        "array type for the instruction.", arrayRegisterType.type.getClassType()));
2561            }
2562        }
2563    }
2564
2565    private void analyze32BitPrimitiveIget(AnalyzedInstruction analyzedInstruction,
2566                                             RegisterType.Category instructionCategory) {
2567        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
2568                RegisterType.getRegisterType(instructionCategory, null));
2569    }
2570
2571    private void verify32BitPrimitiveIget(AnalyzedInstruction analyzedInstruction,
2572                                             RegisterType.Category instructionCategory) {
2573        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
2574
2575        RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
2576                ReferenceOrUninitThisCategories);
2577
2578        //TODO: check access
2579        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
2580        assert referencedItem instanceof FieldIdItem;
2581        FieldIdItem field = (FieldIdItem)referencedItem;
2582
2583        if (objectRegisterType.category != RegisterType.Category.Null &&
2584            !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) {
2585            throw new ValidationException(String.format("Cannot access field %s through type %s",
2586                    field.getFieldString(), objectRegisterType.type.getClassType()));
2587        }
2588
2589        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
2590
2591        if (!checkArrayFieldAssignment(fieldType.category, instructionCategory)) {
2592                throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
2593                        "for the instruction.", analyzedInstruction.instruction.opcode.name,
2594                        field.getFieldString()));
2595        }
2596    }
2597
2598    private void analyzeIgetWideObject(AnalyzedInstruction analyzedInstruction) {
2599        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
2600
2601        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
2602        assert referencedItem instanceof FieldIdItem;
2603        FieldIdItem field = (FieldIdItem)referencedItem;
2604
2605        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
2606        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, fieldType);
2607    }
2608
2609    private void verifyIgetWide(AnalyzedInstruction analyzedInstruction) {
2610        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
2611
2612        RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
2613                ReferenceOrUninitThisCategories);
2614
2615        //TODO: check access
2616        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
2617        assert referencedItem instanceof FieldIdItem;
2618        FieldIdItem field = (FieldIdItem)referencedItem;
2619
2620        if (objectRegisterType.category != RegisterType.Category.Null &&
2621            !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) {
2622            throw new ValidationException(String.format("Cannot access field %s through type %s",
2623                    field.getFieldString(), objectRegisterType.type.getClassType()));
2624        }
2625
2626        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
2627
2628        if (!WideLowCategories.contains(fieldType.category)) {
2629            throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
2630                    "for the instruction.", analyzedInstruction.instruction.opcode.name,
2631                    field.getFieldString()));
2632        }
2633    }
2634
2635    private void verifyIgetObject(AnalyzedInstruction analyzedInstruction) {
2636        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
2637
2638        RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
2639                ReferenceOrUninitThisCategories);
2640
2641        //TODO: check access
2642        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
2643        assert referencedItem instanceof FieldIdItem;
2644        FieldIdItem field = (FieldIdItem)referencedItem;
2645
2646        if (objectRegisterType.category != RegisterType.Category.Null &&
2647            !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) {
2648            throw new ValidationException(String.format("Cannot access field %s through type %s",
2649                    field.getFieldString(), objectRegisterType.type.getClassType()));
2650        }
2651
2652        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
2653
2654        if (fieldType.category != RegisterType.Category.Reference) {
2655            throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
2656                        "for the instruction.", analyzedInstruction.instruction.opcode.name,
2657                        field.getFieldString()));
2658        }
2659    }
2660
2661    private void verify32BitPrimitiveIput(AnalyzedInstruction analyzedInstruction,
2662                                             RegisterType.Category instructionCategory) {
2663        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
2664
2665        RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
2666                ReferenceOrUninitThisCategories);
2667
2668        RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA());
2669        assert sourceRegisterType != null;
2670
2671        //per CodeVerify.c in dalvik:
2672        //java generates synthetic functions that write byte values into boolean fields
2673        if (sourceRegisterType.category == RegisterType.Category.Byte &&
2674            instructionCategory == RegisterType.Category.Boolean) {
2675
2676            sourceRegisterType = RegisterType.getRegisterType(RegisterType.Category.Boolean, null);
2677        }
2678
2679        RegisterType instructionRegisterType = RegisterType.getRegisterType(instructionCategory, null);
2680        if (!sourceRegisterType.canBeAssignedTo(instructionRegisterType)) {
2681            throw new ValidationException(String.format("Cannot use %s with source register type %s.",
2682                    analyzedInstruction.instruction.opcode.name, sourceRegisterType.toString()));
2683        }
2684
2685
2686        //TODO: check access
2687        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
2688        assert referencedItem instanceof FieldIdItem;
2689        FieldIdItem field = (FieldIdItem)referencedItem;
2690
2691        if (objectRegisterType.category != RegisterType.Category.Null &&
2692            !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) {
2693            throw new ValidationException(String.format("Cannot access field %s through type %s",
2694                    field.getFieldString(), objectRegisterType.type.getClassType()));
2695        }
2696
2697        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
2698
2699        if (!checkArrayFieldAssignment(fieldType.category, instructionCategory)) {
2700                throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
2701                        "for the instruction.", analyzedInstruction.instruction.opcode.name,
2702                        field.getFieldString()));
2703        }
2704    }
2705
2706    private void verifyIputWide(AnalyzedInstruction analyzedInstruction) {
2707        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
2708
2709        RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
2710                ReferenceOrUninitThisCategories);
2711
2712        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), WideLowCategories);
2713
2714        //TODO: check access
2715        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
2716        assert referencedItem instanceof FieldIdItem;
2717        FieldIdItem field = (FieldIdItem)referencedItem;
2718
2719        if (objectRegisterType.category != RegisterType.Category.Null &&
2720                !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) {
2721            throw new ValidationException(String.format("Cannot access field %s through type %s",
2722                    field.getFieldString(), objectRegisterType.type.getClassType()));
2723        }
2724
2725        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
2726
2727        if (!WideLowCategories.contains(fieldType.category)) {
2728            throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
2729                    "for the instruction.", analyzedInstruction.instruction.opcode.name,
2730                    field.getFieldString()));
2731        }
2732    }
2733
2734    private void verifyIputObject(AnalyzedInstruction analyzedInstruction) {
2735        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
2736
2737        RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
2738                ReferenceOrUninitThisCategories);
2739
2740        RegisterType sourceRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(),
2741                ReferenceCategories);
2742
2743        //TODO: check access
2744        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
2745        assert referencedItem instanceof FieldIdItem;
2746        FieldIdItem field = (FieldIdItem)referencedItem;
2747
2748        if (objectRegisterType.category != RegisterType.Category.Null &&
2749            !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) {
2750            throw new ValidationException(String.format("Cannot access field %s through type %s",
2751                    field.getFieldString(), objectRegisterType.type.getClassType()));
2752        }
2753
2754        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
2755
2756        if (fieldType.category != RegisterType.Category.Reference) {
2757            throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
2758                        "for the instruction.", analyzedInstruction.instruction.opcode.name,
2759                        field.getFieldString()));
2760        }
2761
2762        if (sourceRegisterType.category != RegisterType.Category.Null &&
2763            !fieldType.type.isInterface() &&
2764            !sourceRegisterType.type.extendsClass(fieldType.type)) {
2765
2766            throw new ValidationException(String.format("Cannot store a value of type %s into a field of type %s",
2767                    sourceRegisterType.type.getClassType(), fieldType.type.getClassType()));
2768        }
2769    }
2770
2771    private void analyze32BitPrimitiveSget(AnalyzedInstruction analyzedInstruction,
2772                                             RegisterType.Category instructionCategory) {
2773        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
2774                RegisterType.getRegisterType(instructionCategory, null));
2775    }
2776
2777    private void verify32BitPrimitiveSget(AnalyzedInstruction analyzedInstruction,
2778                                             RegisterType.Category instructionCategory) {
2779        //TODO: check access
2780        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
2781        assert referencedItem instanceof FieldIdItem;
2782        FieldIdItem field = (FieldIdItem)referencedItem;
2783
2784        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
2785
2786        if (!checkArrayFieldAssignment(fieldType.category, instructionCategory)) {
2787                throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
2788                        "for the instruction.", analyzedInstruction.instruction.opcode.name,
2789                        field.getFieldString()));
2790        }
2791    }
2792
2793    private void analyzeSgetWideObject(AnalyzedInstruction analyzedInstruction) {
2794        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
2795        assert referencedItem instanceof FieldIdItem;
2796        FieldIdItem field = (FieldIdItem)referencedItem;
2797
2798        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
2799        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, fieldType);
2800    }
2801
2802    private void verifySgetWide(AnalyzedInstruction analyzedInstruction) {
2803        //TODO: check access
2804        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
2805        assert referencedItem instanceof FieldIdItem;
2806        FieldIdItem field = (FieldIdItem)referencedItem;
2807
2808        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
2809
2810
2811        if (fieldType.category != RegisterType.Category.LongLo &&
2812            fieldType.category != RegisterType.Category.DoubleLo) {
2813
2814            throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
2815                    "for the instruction.", analyzedInstruction.instruction.opcode.name,
2816                    field.getFieldString()));
2817        }
2818    }
2819
2820    private void verifySgetObject(AnalyzedInstruction analyzedInstruction) {
2821        //TODO: check access
2822        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
2823        assert referencedItem instanceof FieldIdItem;
2824        FieldIdItem field = (FieldIdItem)referencedItem;
2825
2826        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
2827
2828        if (fieldType.category != RegisterType.Category.Reference) {
2829                throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
2830                        "for the instruction.", analyzedInstruction.instruction.opcode.name,
2831                        field.getFieldString()));
2832        }
2833    }
2834
2835    private void verify32BitPrimitiveSput(AnalyzedInstruction analyzedInstruction,
2836                                             RegisterType.Category instructionCategory) {
2837        SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
2838
2839        RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA());
2840        assert sourceRegisterType != null;
2841
2842        //per CodeVerify.c in dalvik:
2843        //java generates synthetic functions that write byte values into boolean fields
2844        if (sourceRegisterType.category == RegisterType.Category.Byte &&
2845            instructionCategory == RegisterType.Category.Boolean) {
2846
2847            sourceRegisterType = RegisterType.getRegisterType(RegisterType.Category.Boolean, null);
2848        }
2849
2850        RegisterType instructionRegisterType = RegisterType.getRegisterType(instructionCategory, null);
2851        if (!sourceRegisterType.canBeAssignedTo(instructionRegisterType)) {
2852            throw new ValidationException(String.format("Cannot use %s with source register type %s.",
2853                    analyzedInstruction.instruction.opcode.name, sourceRegisterType.toString()));
2854        }
2855
2856        //TODO: check access
2857        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
2858        assert referencedItem instanceof FieldIdItem;
2859        FieldIdItem field = (FieldIdItem)referencedItem;
2860
2861        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
2862
2863        if (!checkArrayFieldAssignment(fieldType.category, instructionCategory)) {
2864                throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
2865                        "for the instruction.", analyzedInstruction.instruction.opcode.name,
2866                        field.getFieldString()));
2867        }
2868    }
2869
2870    private void verifySputWide(AnalyzedInstruction analyzedInstruction) {
2871        SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
2872
2873
2874        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), WideLowCategories);
2875
2876        //TODO: check access
2877        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
2878        assert referencedItem instanceof FieldIdItem;
2879        FieldIdItem field = (FieldIdItem)referencedItem;
2880
2881        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
2882
2883        if (!WideLowCategories.contains(fieldType.category)) {
2884                throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
2885                        "for the instruction.", analyzedInstruction.instruction.opcode.name,
2886                        field.getFieldString()));
2887        }
2888    }
2889
2890    private void verifySputObject(AnalyzedInstruction analyzedInstruction) {
2891        SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
2892
2893        RegisterType sourceRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(),
2894                ReferenceCategories);
2895
2896        //TODO: check access
2897        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
2898        assert referencedItem instanceof FieldIdItem;
2899        FieldIdItem field = (FieldIdItem)referencedItem;
2900
2901        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
2902
2903        if (fieldType.category != RegisterType.Category.Reference) {
2904            throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
2905                        "for the instruction.", analyzedInstruction.instruction.opcode.name,
2906                        field.getFieldString()));
2907        }
2908
2909        if (sourceRegisterType.category != RegisterType.Category.Null &&
2910            !fieldType.type.isInterface() &&
2911            !sourceRegisterType.type.extendsClass(fieldType.type)) {
2912
2913            throw new ValidationException(String.format("Cannot store a value of type %s into a field of type %s",
2914                    sourceRegisterType.type.getClassType(), fieldType.type.getClassType()));
2915        }
2916    }
2917
2918    private void analyzeInvokeDirect(AnalyzedInstruction analyzedInstruction) {
2919        FiveRegisterInstruction instruction = (FiveRegisterInstruction)analyzedInstruction.instruction;
2920        analyzeInvokeDirectCommon(analyzedInstruction, new Format35cRegisterIterator(instruction));
2921    }
2922
2923    private void verifyInvoke(AnalyzedInstruction analyzedInstruction, int invokeType) {
2924        FiveRegisterInstruction instruction = (FiveRegisterInstruction)analyzedInstruction.instruction;
2925        verifyInvokeCommon(analyzedInstruction, false, invokeType, new Format35cRegisterIterator(instruction));
2926    }
2927
2928    private void analyzeInvokeDirectRange(AnalyzedInstruction analyzedInstruction) {
2929        RegisterRangeInstruction instruction = (RegisterRangeInstruction)analyzedInstruction.instruction;
2930        analyzeInvokeDirectCommon(analyzedInstruction, new Format3rcRegisterIterator(instruction));
2931    }
2932
2933    private void verifyInvokeRange(AnalyzedInstruction analyzedInstruction, int invokeType) {
2934        RegisterRangeInstruction instruction = (RegisterRangeInstruction)analyzedInstruction.instruction;
2935        verifyInvokeCommon(analyzedInstruction, true, invokeType, new Format3rcRegisterIterator(instruction));
2936    }
2937
2938    private static final int INVOKE_VIRTUAL = 0x01;
2939    private static final int INVOKE_SUPER = 0x02;
2940    private static final int INVOKE_DIRECT = 0x04;
2941    private static final int INVOKE_INTERFACE = 0x08;
2942    private static final int INVOKE_STATIC = 0x10;
2943
2944    private void analyzeInvokeDirectCommon(AnalyzedInstruction analyzedInstruction, RegisterIterator registers) {
2945        //the only time that an invoke instruction changes a register type is when using invoke-direct on a
2946        //constructor (<init>) method, which changes the uninitialized reference (and any register that the same
2947        //uninit reference has been copied to) to an initialized reference
2948
2949        InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
2950
2951        Item item = instruction.getReferencedItem();
2952        assert item.getItemType() == ItemType.TYPE_METHOD_ID_ITEM;
2953        MethodIdItem methodIdItem = (MethodIdItem)item;
2954
2955        if (!methodIdItem.getMethodName().getStringValue().equals("<init>")) {
2956            return;
2957        }
2958
2959        RegisterType objectRegisterType;
2960        //the object register is always the first register
2961        int objectRegister = registers.getRegister();
2962
2963        objectRegisterType = analyzedInstruction.getPreInstructionRegisterType(objectRegister);
2964        assert objectRegisterType != null;
2965
2966        if (objectRegisterType.category != RegisterType.Category.UninitRef &&
2967                objectRegisterType.category != RegisterType.Category.UninitThis) {
2968            return;
2969        }
2970
2971        setPostRegisterTypeAndPropagateChanges(analyzedInstruction, objectRegister,
2972                RegisterType.getRegisterType(RegisterType.Category.Reference, objectRegisterType.type));
2973
2974        for (int i=0; i<analyzedInstruction.postRegisterMap.length; i++) {
2975            RegisterType postInstructionRegisterType = analyzedInstruction.postRegisterMap[i];
2976            if (postInstructionRegisterType.category == RegisterType.Category.Unknown) {
2977                RegisterType preInstructionRegisterType =
2978                        analyzedInstruction.getPreInstructionRegisterType(i);
2979
2980                if (preInstructionRegisterType.category == RegisterType.Category.UninitRef ||
2981                    preInstructionRegisterType.category == RegisterType.Category.UninitThis) {
2982
2983                    RegisterType registerType;
2984                    if (preInstructionRegisterType == objectRegisterType) {
2985                        registerType = analyzedInstruction.postRegisterMap[objectRegister];
2986                    } else {
2987                        registerType = preInstructionRegisterType;
2988                    }
2989
2990                    setPostRegisterTypeAndPropagateChanges(analyzedInstruction, i, registerType);
2991                }
2992            }
2993        }
2994    }
2995
2996    private void verifyInvokeCommon(AnalyzedInstruction analyzedInstruction, boolean isRange, int invokeType,
2997                                       RegisterIterator registers) {
2998        InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
2999
3000        //TODO: check access
3001
3002        Item item = instruction.getReferencedItem();
3003        assert item.getItemType() == ItemType.TYPE_METHOD_ID_ITEM;
3004        MethodIdItem methodIdItem = (MethodIdItem)item;
3005
3006        TypeIdItem methodClass = methodIdItem.getContainingClass();
3007        boolean isInit = false;
3008
3009        if (methodIdItem.getMethodName().getStringValue().charAt(0) == '<') {
3010            if ((invokeType & INVOKE_DIRECT) != 0) {
3011                isInit = true;
3012            } else {
3013                throw new ValidationException(String.format("Cannot call constructor %s with %s",
3014                        methodIdItem.getMethodString(), analyzedInstruction.instruction.opcode.name));
3015            }
3016        }
3017
3018        ClassPath.ClassDef methodClassDef = ClassPath.getClassDef(methodClass);
3019        if ((invokeType & INVOKE_INTERFACE) != 0) {
3020            if (!methodClassDef.isInterface()) {
3021                throw new ValidationException(String.format("Cannot call method %s with %s. %s is not an interface " +
3022                        "class.", methodIdItem.getMethodString(), analyzedInstruction.instruction.opcode.name,
3023                        methodClassDef.getClassType()));
3024            }
3025        } else {
3026            if (methodClassDef.isInterface()) {
3027                throw new ValidationException(String.format("Cannot call method %s with %s. %s is an interface class." +
3028                        " Use invoke-interface or invoke-interface/range instead.", methodIdItem.getMethodString(),
3029                        analyzedInstruction.instruction.opcode.name, methodClassDef.getClassType()));
3030            }
3031        }
3032
3033        if ((invokeType & INVOKE_SUPER) != 0) {
3034            ClassPath.ClassDef currentMethodClassDef = ClassPath.getClassDef(encodedMethod.method.getContainingClass());
3035            if (currentMethodClassDef.getSuperclass() == null) {
3036                throw new ValidationException(String.format("Cannot call method %s with %s. %s has no superclass",
3037                        methodIdItem.getMethodString(), analyzedInstruction.instruction.opcode.name,
3038                        methodClassDef.getSuperclass().getClassType()));
3039            }
3040
3041            if (!currentMethodClassDef.getSuperclass().extendsClass(methodClassDef)) {
3042                throw new ValidationException(String.format("Cannot call method %s with %s. %s is not an ancestor " +
3043                        "of the current class %s", methodIdItem.getMethodString(),
3044                        analyzedInstruction.instruction.opcode.name, methodClass.getTypeDescriptor(),
3045                        encodedMethod.method.getContainingClass().getTypeDescriptor()));
3046            }
3047
3048            if (!currentMethodClassDef.getSuperclass().hasVirtualMethod(methodIdItem.getVirtualMethodString())) {
3049                throw new ValidationException(String.format("Cannot call method %s with %s. The superclass %s has" +
3050                        "no such method", methodIdItem.getMethodString(),
3051                        analyzedInstruction.instruction.opcode.name, methodClassDef.getSuperclass().getClassType()));
3052            }
3053        }
3054
3055        assert isRange || registers.getCount() <= 5;
3056
3057        TypeListItem typeListItem = methodIdItem.getPrototype().getParameters();
3058        int methodParameterRegisterCount;
3059        if (typeListItem == null) {
3060            methodParameterRegisterCount = 0;
3061        } else {
3062            methodParameterRegisterCount = typeListItem.getRegisterCount();
3063        }
3064
3065        if ((invokeType & INVOKE_STATIC) == 0) {
3066            methodParameterRegisterCount++;
3067        }
3068
3069        if (methodParameterRegisterCount != registers.getCount()) {
3070            throw new ValidationException(String.format("The number of registers does not match the number of " +
3071                    "parameters for method %s. Expecting %d registers, got %d.", methodIdItem.getMethodString(),
3072                    methodParameterRegisterCount + 1, registers.getCount()));
3073        }
3074
3075        RegisterType objectRegisterType = null;
3076        int objectRegister = 0;
3077        if ((invokeType & INVOKE_STATIC) == 0) {
3078            objectRegister = registers.getRegister();
3079            registers.moveNext();
3080
3081            objectRegisterType = analyzedInstruction.getPreInstructionRegisterType(objectRegister);
3082            assert objectRegisterType != null;
3083            if (objectRegisterType.category == RegisterType.Category.UninitRef ||
3084                    objectRegisterType.category == RegisterType.Category.UninitThis) {
3085
3086                if (!isInit) {
3087                    throw new ValidationException(String.format("Cannot invoke non-<init> method %s on uninitialized " +
3088                            "reference type %s", methodIdItem.getMethodString(),
3089                            objectRegisterType.type.getClassType()));
3090                }
3091            } else if (objectRegisterType.category == RegisterType.Category.Reference) {
3092                if (isInit) {
3093                    throw new ValidationException(String.format("Cannot invoke %s on initialized reference type %s",
3094                            methodIdItem.getMethodString(), objectRegisterType.type.getClassType()));
3095                }
3096            } else if (objectRegisterType.category == RegisterType.Category.Null) {
3097                if (isInit) {
3098                    throw new ValidationException(String.format("Cannot invoke %s on a null reference",
3099                            methodIdItem.getMethodString()));
3100                }
3101            }
3102            else {
3103                throw new ValidationException(String.format("Cannot invoke %s on non-reference type %s",
3104                        methodIdItem.getMethodString(), objectRegisterType.toString()));
3105            }
3106
3107            if (isInit) {
3108                if (objectRegisterType.type.getSuperclass() == methodClassDef) {
3109                    if (!encodedMethod.method.getMethodName().getStringValue().equals("<init>")) {
3110                        throw new ValidationException(String.format("Cannot call %s on type %s. The object type must " +
3111                                "match the method type exactly", methodIdItem.getMethodString(),
3112                                objectRegisterType.type.getClassType()));
3113                    }
3114                }
3115            }
3116
3117            if ((invokeType & INVOKE_INTERFACE) == 0 && objectRegisterType.category != RegisterType.Category.Null &&
3118                    !objectRegisterType.type.extendsClass(methodClassDef)) {
3119
3120               throw new ValidationException(String.format("Cannot call method %s on an object of type %s, which " +
3121                       "does not extend %s.", methodIdItem.getMethodString(), objectRegisterType.type.getClassType(),
3122                        methodClassDef.getClassType()));
3123            }
3124        }
3125
3126        if (typeListItem != null) {
3127            List<TypeIdItem> parameterTypes = typeListItem.getTypes();
3128            int parameterTypeIndex = 0;
3129            while (!registers.pastEnd()) {
3130                assert parameterTypeIndex < parameterTypes.size();
3131                RegisterType parameterType =
3132                        RegisterType.getRegisterTypeForTypeIdItem(parameterTypes.get(parameterTypeIndex));
3133
3134                int register = registers.getRegister();
3135
3136                RegisterType parameterRegisterType;
3137                if (WideLowCategories.contains(parameterType.category)) {
3138                    parameterRegisterType = getAndCheckSourceRegister(analyzedInstruction, register, WideLowCategories);
3139
3140                    if (!registers.moveNext()) {
3141                        throw new ValidationException(String.format("No 2nd register specified for wide register pair v%d",
3142                                parameterTypeIndex+1));
3143                    }
3144                    int nextRegister = registers.getRegister();
3145
3146                    if (nextRegister != register + 1) {
3147                        throw new ValidationException(String.format("Invalid wide register pair (v%d, v%d). Registers " +
3148                                "must be consecutive.", register, nextRegister));
3149                    }
3150                } else {
3151                    parameterRegisterType = analyzedInstruction.getPreInstructionRegisterType(register);
3152                }
3153
3154                assert parameterRegisterType != null;
3155
3156                if (!parameterRegisterType.canBeAssignedTo(parameterType)) {
3157                    throw new ValidationException(
3158                            String.format("Invalid register type %s for parameter %d %s.",
3159                                    parameterRegisterType.toString(), parameterTypeIndex+1,
3160                                    parameterType.toString()));
3161                }
3162
3163                parameterTypeIndex++;
3164                registers.moveNext();
3165            }
3166        }
3167    }
3168
3169    private void analyzeUnaryOp(AnalyzedInstruction analyzedInstruction, RegisterType.Category destRegisterCategory) {
3170        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
3171                RegisterType.getRegisterType(destRegisterCategory, null));
3172    }
3173
3174    private void verifyUnaryOp(AnalyzedInstruction analyzedInstruction, EnumSet validSourceCategories) {
3175        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
3176
3177        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), validSourceCategories);
3178    }
3179
3180    private void analyzeBinaryOp(AnalyzedInstruction analyzedInstruction, RegisterType.Category destRegisterCategory,
3181                                boolean checkForBoolean) {
3182        if (checkForBoolean) {
3183            ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
3184
3185            RegisterType source1RegisterType =
3186                    analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
3187            RegisterType source2RegisterType =
3188                    analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterC());
3189
3190            if (BooleanCategories.contains(source1RegisterType.category) &&
3191                BooleanCategories.contains(source2RegisterType.category)) {
3192
3193                destRegisterCategory = RegisterType.Category.Boolean;
3194            }
3195        }
3196
3197        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
3198                RegisterType.getRegisterType(destRegisterCategory, null));
3199    }
3200
3201    private void verifyBinaryOp(AnalyzedInstruction analyzedInstruction, EnumSet validSource1Categories,
3202                                EnumSet validSource2Categories) {
3203        ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
3204
3205        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), validSource1Categories);
3206        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), validSource2Categories);
3207    }
3208
3209    private void analyzeBinary2AddrOp(AnalyzedInstruction analyzedInstruction,
3210                                      RegisterType.Category destRegisterCategory, boolean checkForBoolean) {
3211        if (checkForBoolean) {
3212            TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
3213
3214            RegisterType source1RegisterType =
3215                    analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA());
3216            RegisterType source2RegisterType =
3217                    analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
3218
3219            if (BooleanCategories.contains(source1RegisterType.category) &&
3220                BooleanCategories.contains(source2RegisterType.category)) {
3221
3222                destRegisterCategory = RegisterType.Category.Boolean;
3223            }
3224        }
3225
3226        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
3227                RegisterType.getRegisterType(destRegisterCategory, null));
3228    }
3229
3230    private void verifyBinary2AddrOp(AnalyzedInstruction analyzedInstruction, EnumSet validSource1Categories,
3231                                EnumSet validSource2Categories) {
3232        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
3233
3234        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), validSource1Categories);
3235        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), validSource2Categories);
3236    }
3237
3238    private void analyzeLiteralBinaryOp(AnalyzedInstruction analyzedInstruction,
3239                                        RegisterType.Category destRegisterCategory, boolean checkForBoolean) {
3240        if (checkForBoolean) {
3241            TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
3242
3243            RegisterType sourceRegisterType =
3244                    analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
3245
3246            if (BooleanCategories.contains(sourceRegisterType.category)) {
3247                long literal = ((LiteralInstruction)analyzedInstruction.instruction).getLiteral();
3248                if (literal == 0 || literal == 1) {
3249                    destRegisterCategory = RegisterType.Category.Boolean;
3250                }
3251            }
3252        }
3253
3254        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
3255                RegisterType.getRegisterType(destRegisterCategory, null));
3256    }
3257
3258    private void verifyLiteralBinaryOp(AnalyzedInstruction analyzedInstruction) {
3259        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
3260
3261        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), Primitive32BitCategories);
3262    }
3263
3264    private RegisterType.Category getDestTypeForLiteralShiftRight(AnalyzedInstruction analyzedInstruction,
3265                                                                  boolean signedShift) {
3266        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
3267
3268        RegisterType sourceRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
3269                Primitive32BitCategories);
3270        long literalShift = ((LiteralInstruction)analyzedInstruction.instruction).getLiteral();
3271
3272        if (literalShift == 0) {
3273            return sourceRegisterType.category;
3274        }
3275
3276        RegisterType.Category destRegisterCategory;
3277        if (!signedShift) {
3278            destRegisterCategory = RegisterType.Category.Integer;
3279        } else {
3280            destRegisterCategory = sourceRegisterType.category;
3281        }
3282
3283        if (literalShift >= 32) {
3284            //TODO: add warning
3285            return destRegisterCategory;
3286        }
3287
3288        switch (sourceRegisterType.category) {
3289            case Integer:
3290            case Float:
3291                if (!signedShift) {
3292                    if (literalShift > 24) {
3293                        return RegisterType.Category.PosByte;
3294                    }
3295                    if (literalShift >= 16) {
3296                        return RegisterType.Category.Char;
3297                    }
3298                } else {
3299                    if (literalShift >= 24) {
3300                        return RegisterType.Category.Byte;
3301                    }
3302                    if (literalShift >= 16) {
3303                        return RegisterType.Category.Short;
3304                    }
3305                }
3306                break;
3307            case Short:
3308                if (signedShift && literalShift >= 8) {
3309                    return RegisterType.Category.Byte;
3310                }
3311                break;
3312            case PosShort:
3313                if (literalShift >= 8) {
3314                    return RegisterType.Category.PosByte;
3315                }
3316                break;
3317            case Char:
3318                if (literalShift > 8) {
3319                    return RegisterType.Category.PosByte;
3320                }
3321                break;
3322            case Byte:
3323                break;
3324            case PosByte:
3325                return RegisterType.Category.PosByte;
3326            case Null:
3327            case One:
3328            case Boolean:
3329                return RegisterType.Category.Null;
3330            default:
3331                assert false;
3332        }
3333
3334        return destRegisterCategory;
3335    }
3336
3337
3338    private void analyzeExecuteInline(AnalyzedInstruction analyzedInstruction) {
3339        if (deodexUtil == null) {
3340            throw new ValidationException("Cannot analyze an odexed instruction unless we are deodexing");
3341        }
3342
3343        Instruction35mi instruction = (Instruction35mi)analyzedInstruction.instruction;
3344
3345        DeodexUtil.InlineMethod inlineMethod = deodexUtil.lookupInlineMethod(analyzedInstruction);
3346        MethodIdItem inlineMethodIdItem = inlineMethod.getMethodIdItem();
3347        if (inlineMethodIdItem == null) {
3348            throw new ValidationException(String.format("Cannot load inline method with index %d",
3349                    instruction.getInlineIndex()));
3350        }
3351
3352        Opcode deodexedOpcode = null;
3353        switch (inlineMethod.methodType) {
3354            case DeodexUtil.Direct:
3355                deodexedOpcode = Opcode.INVOKE_DIRECT;
3356                break;
3357            case DeodexUtil.Static:
3358                deodexedOpcode = Opcode.INVOKE_STATIC;
3359                break;
3360            case DeodexUtil.Virtual:
3361                deodexedOpcode = Opcode.INVOKE_VIRTUAL;
3362                break;
3363            default:
3364                assert false;
3365        }
3366
3367        Instruction35c deodexedInstruction = new Instruction35c(deodexedOpcode, instruction.getRegCount(),
3368                instruction.getRegisterD(), instruction.getRegisterE(), instruction.getRegisterF(),
3369                instruction.getRegisterG(), instruction.getRegisterA(), inlineMethodIdItem);
3370
3371        analyzedInstruction.setDeodexedInstruction(deodexedInstruction);
3372
3373        analyzeInstruction(analyzedInstruction);
3374    }
3375
3376    private void analyzeExecuteInlineRange(AnalyzedInstruction analyzedInstruction) {
3377        if (deodexUtil == null) {
3378            throw new ValidationException("Cannot analyze an odexed instruction unless we are deodexing");
3379        }
3380
3381        Instruction3rmi instruction = (Instruction3rmi)analyzedInstruction.instruction;
3382
3383        DeodexUtil.InlineMethod inlineMethod = deodexUtil.lookupInlineMethod(analyzedInstruction);
3384        MethodIdItem inlineMethodIdItem = inlineMethod.getMethodIdItem();
3385        if (inlineMethodIdItem == null) {
3386            throw new ValidationException(String.format("Cannot load inline method with index %d",
3387                    instruction.getInlineIndex()));
3388        }
3389
3390        Opcode deodexedOpcode = null;
3391        switch (inlineMethod.methodType) {
3392            case DeodexUtil.Direct:
3393                deodexedOpcode = Opcode.INVOKE_DIRECT_RANGE;
3394                break;
3395            case DeodexUtil.Static:
3396                deodexedOpcode = Opcode.INVOKE_STATIC_RANGE;
3397                break;
3398            case DeodexUtil.Virtual:
3399                deodexedOpcode = Opcode.INVOKE_VIRTUAL_RANGE;
3400                break;
3401            default:
3402                assert false;
3403        }
3404
3405        Instruction3rc deodexedInstruction = new Instruction3rc(deodexedOpcode, instruction.getRegCount(),
3406                instruction.getStartRegister(), inlineMethodIdItem);
3407
3408        analyzedInstruction.setDeodexedInstruction(deodexedInstruction);
3409
3410        analyzeInstruction(analyzedInstruction);
3411    }
3412
3413    private void analyzeInvokeDirectEmpty(AnalyzedInstruction analyzedInstruction) {
3414        Instruction35s instruction = (Instruction35s)analyzedInstruction.instruction;
3415
3416        Instruction35c deodexedInstruction = new Instruction35c(Opcode.INVOKE_DIRECT, instruction.getRegCount(),
3417                instruction.getRegisterD(), instruction.getRegisterE(), instruction.getRegisterF(),
3418                instruction.getRegisterG(), instruction.getRegisterA(), instruction.getReferencedItem());
3419
3420        analyzedInstruction.setDeodexedInstruction(deodexedInstruction);
3421
3422        analyzeInstruction(analyzedInstruction);
3423    }
3424
3425    private boolean analyzeIputIgetQuick(AnalyzedInstruction analyzedInstruction) {
3426        Instruction22cs instruction = (Instruction22cs)analyzedInstruction.instruction;
3427
3428        int fieldOffset = instruction.getFieldOffset();
3429        RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
3430                ReferenceOrUninitCategories);
3431
3432        if (objectRegisterType.category == RegisterType.Category.Null) {
3433            return false;
3434        }
3435
3436        FieldIdItem fieldIdItem = deodexUtil.lookupField(objectRegisterType.type, fieldOffset);
3437        if (fieldIdItem == null) {
3438            throw new ValidationException(String.format("Could not resolve the field in class %s at offset %d",
3439                    objectRegisterType.type.getClassType(), fieldOffset));
3440        }
3441
3442        String fieldType = fieldIdItem.getFieldType().getTypeDescriptor();
3443
3444        Opcode opcode = OdexedFieldInstructionMapper.getAndCheckDeodexedOpcodeForOdexedOpcode(fieldType, instruction.opcode);
3445
3446        Instruction22c deodexedInstruction = new Instruction22c(opcode, (byte)instruction.getRegisterA(),
3447                (byte)instruction.getRegisterB(), fieldIdItem);
3448        analyzedInstruction.setDeodexedInstruction(deodexedInstruction);
3449
3450        analyzeInstruction(analyzedInstruction);
3451
3452        return true;
3453    }
3454
3455    private boolean analyzeInvokeVirtualQuick(AnalyzedInstruction analyzedInstruction, boolean isSuper,
3456                                              boolean isRange) {
3457        int methodIndex;
3458        int objectRegister;
3459
3460
3461        if (isRange) {
3462            Instruction3rms instruction = (Instruction3rms)analyzedInstruction.instruction;
3463            methodIndex = instruction.getVtableIndex();
3464            objectRegister = instruction.getStartRegister();
3465        } else {
3466            Instruction35ms instruction = (Instruction35ms)analyzedInstruction.instruction;
3467            methodIndex = instruction.getVtableIndex();
3468            objectRegister = instruction.getRegisterD();
3469        }
3470
3471        RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, objectRegister,
3472                ReferenceOrUninitCategories);
3473
3474        if (objectRegisterType.category == RegisterType.Category.Null) {
3475            return false;
3476        }
3477
3478        MethodIdItem methodIdItem = null;
3479        if (isSuper) {
3480            ClassPath.ClassDef classDef = ClassPath.getClassDef(this.encodedMethod.method.getContainingClass(), false);
3481            assert classDef != null;
3482
3483            if (classDef.getSuperclass() != null) {
3484                methodIdItem = deodexUtil.lookupVirtualMethod(classDef.getSuperclass(), methodIndex);
3485            }
3486
3487            if (methodIdItem == null) {
3488                //it's possible that the pre-odexed instruction had used the method from the current class instead
3489                //of from the superclass (although the superclass method is still what would actually be called).
3490                //And so the MethodIdItem for the superclass method may not be in the dex file. Let's try to get the
3491                //MethodIdItem for the method in the current class instead
3492                methodIdItem = deodexUtil.lookupVirtualMethod(classDef, methodIndex);
3493            }
3494        } else{
3495            methodIdItem = deodexUtil.lookupVirtualMethod(objectRegisterType.type, methodIndex);
3496        }
3497
3498        if (methodIdItem == null) {
3499            throw new ValidationException(String.format("Could not resolve the method in class %s at index %d",
3500                    objectRegisterType.type.getClassType(), methodIndex));
3501        }
3502
3503
3504        Instruction deodexedInstruction;
3505        if (isRange) {
3506            Instruction3rms instruction = (Instruction3rms)analyzedInstruction.instruction;
3507            Opcode opcode;
3508            if (isSuper) {
3509                opcode = Opcode.INVOKE_SUPER_RANGE;
3510            } else {
3511                opcode = Opcode.INVOKE_VIRTUAL_RANGE;
3512            }
3513
3514            deodexedInstruction = new Instruction3rc(opcode, instruction.getRegCount(),
3515                    instruction.getStartRegister(), methodIdItem);
3516        } else {
3517            Instruction35ms instruction = (Instruction35ms)analyzedInstruction.instruction;
3518            Opcode opcode;
3519            if (isSuper) {
3520                opcode = Opcode.INVOKE_SUPER;
3521            } else {
3522                opcode = Opcode.INVOKE_VIRTUAL;
3523            }
3524
3525            deodexedInstruction = new Instruction35c(opcode, instruction.getRegCount(),
3526                    instruction.getRegisterD(), instruction.getRegisterE(), instruction.getRegisterF(),
3527                    instruction.getRegisterG(), instruction.getRegisterA(), methodIdItem);
3528        }
3529
3530        analyzedInstruction.setDeodexedInstruction(deodexedInstruction);
3531        analyzeInstruction(analyzedInstruction);
3532
3533        return true;
3534    }
3535
3536    private boolean analyzePutGetVolatile(AnalyzedInstruction analyzedInstruction) {
3537        FieldIdItem fieldIdItem =
3538                (FieldIdItem)(((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem());
3539
3540        String fieldType = fieldIdItem.getFieldType().getTypeDescriptor();
3541
3542        Opcode opcode = OdexedFieldInstructionMapper.getAndCheckDeodexedOpcodeForOdexedOpcode(fieldType,
3543                analyzedInstruction.instruction.opcode);
3544
3545        Instruction deodexedInstruction;
3546
3547        if (analyzedInstruction.instruction.opcode.isOdexedStaticVolatile()) {
3548            SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
3549
3550            deodexedInstruction = new Instruction21c(opcode, (byte)instruction.getRegisterA(),
3551                fieldIdItem);
3552        } else {
3553            TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
3554
3555            deodexedInstruction = new Instruction22c(opcode, (byte)instruction.getRegisterA(),
3556                (byte)instruction.getRegisterB(), fieldIdItem);
3557        }
3558
3559        analyzedInstruction.setDeodexedInstruction(deodexedInstruction);
3560        analyzeInstruction(analyzedInstruction);
3561
3562        return true;
3563    }
3564
3565    private static boolean checkArrayFieldAssignment(RegisterType.Category arrayFieldCategory,
3566                                                  RegisterType.Category instructionCategory) {
3567        if (arrayFieldCategory == instructionCategory) {
3568            return true;
3569        }
3570
3571        if ((arrayFieldCategory == RegisterType.Category.Integer &&
3572             instructionCategory == RegisterType.Category.Float) ||
3573            (arrayFieldCategory == RegisterType.Category.Float &&
3574             instructionCategory == RegisterType.Category.Integer)) {
3575            return true;
3576        }
3577        return false;
3578    }
3579
3580    private static RegisterType getAndCheckSourceRegister(AnalyzedInstruction analyzedInstruction, int registerNumber,
3581                                            EnumSet validCategories) {
3582        assert registerNumber >= 0 && registerNumber < analyzedInstruction.postRegisterMap.length;
3583
3584        RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(registerNumber);
3585        assert registerType != null;
3586
3587        checkRegister(registerType, registerNumber, validCategories);
3588
3589        if (validCategories == WideLowCategories) {
3590            checkRegister(registerType, registerNumber, WideLowCategories);
3591            checkWidePair(registerNumber, analyzedInstruction);
3592
3593            RegisterType secondRegisterType = analyzedInstruction.getPreInstructionRegisterType(registerNumber + 1);
3594            assert secondRegisterType != null;
3595            checkRegister(secondRegisterType, registerNumber+1, WideHighCategories);
3596        }
3597
3598        return registerType;
3599    }
3600
3601    private static void checkRegister(RegisterType registerType, int registerNumber, EnumSet validCategories) {
3602        if (!validCategories.contains(registerType.category)) {
3603            throw new ValidationException(String.format("Invalid register type %s for register v%d.",
3604                    registerType.toString(), registerNumber));
3605        }
3606    }
3607
3608    private static void checkWidePair(int registerNumber, AnalyzedInstruction analyzedInstruction) {
3609        if (registerNumber + 1 >= analyzedInstruction.postRegisterMap.length) {
3610            throw new ValidationException(String.format("v%d cannot be used as the first register in a wide register" +
3611                    "pair because it is the last register.", registerNumber));
3612        }
3613    }
3614
3615    private static interface RegisterIterator {
3616        int getRegister();
3617        boolean moveNext();
3618        int getCount();
3619        boolean pastEnd();
3620    }
3621
3622    private static class Format35cRegisterIterator implements RegisterIterator {
3623        private final int registerCount;
3624        private final int[] registers;
3625        private int currentRegister = 0;
3626
3627        public Format35cRegisterIterator(FiveRegisterInstruction instruction) {
3628            registerCount = instruction.getRegCount();
3629            registers = new int[]{instruction.getRegisterD(), instruction.getRegisterE(),
3630                                  instruction.getRegisterF(), instruction.getRegisterG(),
3631                                  instruction.getRegisterA()};
3632        }
3633
3634        public int getRegister() {
3635            return registers[currentRegister];
3636        }
3637
3638        public boolean moveNext() {
3639            currentRegister++;
3640            return !pastEnd();
3641        }
3642
3643        public int getCount() {
3644            return registerCount;
3645        }
3646
3647        public boolean pastEnd() {
3648            return currentRegister >= registerCount;
3649        }
3650    }
3651
3652    private static class Format3rcRegisterIterator implements RegisterIterator {
3653        private final int startRegister;
3654        private final int registerCount;
3655        private int currentRegister = 0;
3656
3657        public Format3rcRegisterIterator(RegisterRangeInstruction instruction) {
3658            startRegister = instruction.getStartRegister();
3659            registerCount = instruction.getRegCount();
3660        }
3661
3662        public int getRegister() {
3663            return startRegister + currentRegister;
3664        }
3665
3666        public boolean moveNext() {
3667            currentRegister++;
3668            return !pastEnd();
3669        }
3670
3671        public int getCount() {
3672            return registerCount;
3673        }
3674
3675        public boolean pastEnd() {
3676            return currentRegister >= registerCount;
3677        }
3678    }
3679}
3680