MethodAnalyzer.java revision 00fc68adf2e39aeb9fed35293f2576bbe729ec4b
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.*;
35
36
37import java.util.*;
38
39public class MethodAnalyzer {
40    private final ClassDataItem.EncodedMethod encodedMethod;
41
42    private final DeodexUtil deodexUtil;
43
44    private SparseArray<AnalyzedInstruction> instructions;
45
46    private boolean analyzed = false;
47
48    private BitSet verifiedInstructions;
49
50    private ValidationException validationException = null;
51
52    //This is a dummy instruction that occurs immediately before the first real instruction. We can initialize the
53    //register types for this instruction to the parameter types, in order to have them propagate to all of its
54    //successors, e.g. the first real instruction, the first instructions in any exception handlers covering the first
55    //instruction, etc.
56    private AnalyzedInstruction startOfMethod;
57
58    public MethodAnalyzer(ClassDataItem.EncodedMethod encodedMethod, boolean deodex) {
59        if (encodedMethod == null) {
60            throw new IllegalArgumentException("encodedMethod cannot be null");
61        }
62        if (encodedMethod.codeItem == null || encodedMethod.codeItem.getInstructions().length == 0) {
63            throw new IllegalArgumentException("The method has no code");
64        }
65        this.encodedMethod = encodedMethod;
66
67        if (deodex) {
68            this.deodexUtil = new DeodexUtil(encodedMethod.method.getDexFile());
69        } else {
70            this.deodexUtil = null;
71        }
72
73        //override AnalyzedInstruction and provide custom implementations of some of the methods, so that we don't
74        //have to handle the case this special case of instruction being null, in the main class
75        startOfMethod = new AnalyzedInstruction(null, -1, encodedMethod.codeItem.getRegisterCount()) {
76            public boolean setsRegister() {
77                return false;
78            }
79
80            @Override
81            public boolean setsWideRegister() {
82                return false;
83            }
84
85            @Override
86            public boolean setsRegister(int registerNumber) {
87                return false;
88            }
89
90            @Override
91            public int getDestinationRegister() {
92                assert false;
93                return -1;
94            };
95        };
96
97        buildInstructionList();
98
99        verifiedInstructions = new BitSet(instructions.size());
100    }
101
102    public AnalyzedInstruction[] analyze() {
103        assert encodedMethod != null;
104        assert encodedMethod.codeItem != null;
105
106        if (analyzed) {
107            return makeInstructionArray();
108        }
109
110        CodeItem codeItem = encodedMethod.codeItem;
111        MethodIdItem methodIdItem = encodedMethod.method;
112
113        int totalRegisters = codeItem.getRegisterCount();
114        int parameterRegisters = methodIdItem.getPrototype().getParameterRegisterCount();
115
116        int nonParameterRegisters = totalRegisters - parameterRegisters;
117
118        //if this isn't a static method, determine which register is the "this" register and set the type to the
119        //current class
120        if ((encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) == 0) {
121            nonParameterRegisters--;
122            int thisRegister = totalRegisters - parameterRegisters - 1;
123
124            //if this is a constructor, then set the "this" register to an uninitialized reference of the current class
125            if ((encodedMethod.accessFlags & AccessFlags.CONSTRUCTOR.getValue()) != 0) {
126                //TODO: it would probably make more sense to validate this somewhere else, and just put an assert here. Also, need to do a similar check for static constructor
127                if (!encodedMethod.method.getMethodName().getStringValue().equals("<init>")) {
128                    throw new ValidationException("The constructor flag can only be used with an <init> method.");
129                }
130
131                setPostRegisterTypeAndPropagateChanges(startOfMethod, thisRegister,
132                        RegisterType.getRegisterType(RegisterType.Category.UninitThis,
133                            ClassPath.getClassDef(methodIdItem.getContainingClass())));
134            } else {
135                if (encodedMethod.method.getMethodName().getStringValue().equals("<init>")) {
136                    throw new ValidationException("An <init> method must have the \"constructor\" access flag");
137                }
138
139                setPostRegisterTypeAndPropagateChanges(startOfMethod, thisRegister,
140                        RegisterType.getRegisterType(RegisterType.Category.Reference,
141                            ClassPath.getClassDef(methodIdItem.getContainingClass())));
142            }
143        }
144
145        TypeListItem parameters = methodIdItem.getPrototype().getParameters();
146        if (parameters != null) {
147            RegisterType[] parameterTypes = getParameterTypes(parameters, parameterRegisters);
148            for (int i=0; i<parameterTypes.length; i++) {
149                RegisterType registerType = parameterTypes[i];
150                int registerNum = (totalRegisters - parameterRegisters) + i;
151                setPostRegisterTypeAndPropagateChanges(startOfMethod, registerNum, registerType);
152            }
153        }
154
155        RegisterType uninit = RegisterType.getRegisterType(RegisterType.Category.Uninit, null);
156        for (int i=0; i<nonParameterRegisters; i++) {
157            setPostRegisterTypeAndPropagateChanges(startOfMethod, i, uninit);
158        }
159
160        BitSet instructionsToAnalyze = new BitSet(verifiedInstructions.size());
161
162        //make sure all of the "first instructions" are marked for processing
163        for (AnalyzedInstruction successor: startOfMethod.successors) {
164            instructionsToAnalyze.set(successor.instructionIndex);
165        }
166
167        BitSet odexedInstructions = new BitSet(verifiedInstructions.size());
168
169        do {
170            boolean didSomething = false;
171
172            while (!instructionsToAnalyze.isEmpty()) {
173                for(int i=instructionsToAnalyze.nextSetBit(0); i>=0; i=instructionsToAnalyze.nextSetBit(i+1)) {
174                    instructionsToAnalyze.clear(i);
175                    if (verifiedInstructions.get(i)) {
176                        continue;
177                    }
178                    AnalyzedInstruction instructionToVerify = instructions.valueAt(i);
179                    try {
180                        if (instructionToVerify.originalInstruction.opcode.odexOnly()) {
181                            instructionToVerify.restoreOdexedInstruction();
182                        }
183
184                        if (!analyzeInstruction(instructionToVerify)) {
185                            odexedInstructions.set(i);
186                            continue;
187                        } else {
188                            didSomething = true;
189                            odexedInstructions.clear(i);
190                        }
191                    } catch (ValidationException ex) {
192                        this.validationException = ex;
193                        int codeAddress = getInstructionAddress(instructionToVerify);
194                        ex.setCodeAddress(codeAddress);
195                        ex.addContext(String.format("opcode: %s", instructionToVerify.instruction.opcode.name));
196                        ex.addContext(String.format("CodeAddress: %d", codeAddress));
197                        ex.addContext(String.format("Method: %s", encodedMethod.method.getMethodString()));
198                        break;
199                    }
200
201                    verifiedInstructions.set(instructionToVerify.getInstructionIndex());
202
203                    for (AnalyzedInstruction successor: instructionToVerify.successors) {
204                        instructionsToAnalyze.set(successor.getInstructionIndex());
205                    }
206                }
207                if (validationException != null) {
208                    break;
209                }
210            }
211
212            if (!didSomething) {
213                break;
214            }
215
216            if (!odexedInstructions.isEmpty()) {
217                for (int i=odexedInstructions.nextSetBit(0); i>=0; i=odexedInstructions.nextSetBit(i+1)) {
218                    instructionsToAnalyze.set(i);
219                }
220            }
221        } while (true);
222
223        for (int i=0; i<instructions.size(); i++) {
224            AnalyzedInstruction instruction = instructions.valueAt(i);
225
226            if (!odexedInstructions.get(i)) {
227
228                //TODO: We probably need to re-verify everything after changing unknown-uninit. Better yet, maybe we should split the register propagation logic and the verification logic, and only do the verification after all the register info is known
229
230                //We don't want to change unknown register types to uninit for unreachable instructions, as the register
231                //types for the unreachable instruction shouldn't be taken into account when merging registers for any
232                //reachable predecessor (for example, the predecessor of an unreachable goto instruction)
233                //So we keep the unreachable register types as unknown, because anything else + unknown = anything else
234                if (verifiedInstructions.get(i)) {
235                    for (int j=0; j<instruction.postRegisterMap.length; j++) {
236                        if (instruction.postRegisterMap[j].category == RegisterType.Category.Unknown) {
237                            instruction.postRegisterMap[j] = uninit;
238                        }
239                    }
240                }
241            } else {
242                Instruction odexedInstruction = instruction.instruction;
243                int objectRegisterNumber;
244
245                if (odexedInstruction.getFormat() == Format.Format22cs) {
246                    objectRegisterNumber = ((Instruction22cs)odexedInstruction).getRegisterB();
247                } else if (odexedInstruction.getFormat() == Format.Format35ms) {
248                    objectRegisterNumber = ((Instruction35ms)odexedInstruction).getRegisterD();
249                } else if (odexedInstruction.getFormat() == Format.Format3rms) {
250                    objectRegisterNumber = ((Instruction3rms)odexedInstruction).getStartRegister();
251                } else {
252                    assert false;
253                    throw new ExceptionWithContext(String.format("Unexpected format %s for odexed instruction",
254                            odexedInstruction.getFormat().name()));
255                }
256
257                instruction.setDeodexedInstruction(new UnresolvedNullReference(odexedInstruction,
258                        objectRegisterNumber));
259
260                setAndPropagateDeadness(instruction);
261            }
262        }
263
264        analyzed = true;
265        return makeInstructionArray();
266    }
267
268    private int getThisRegister() {
269        assert (encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) == 0;
270
271        CodeItem codeItem = encodedMethod.codeItem;
272        assert codeItem != null;
273
274        MethodIdItem methodIdItem = encodedMethod.method;
275        assert methodIdItem != null;
276
277        int totalRegisters = codeItem.getRegisterCount();
278        if (totalRegisters == 0) {
279            throw new ValidationException("A non-static method must have at least 1 register");
280        }
281
282        int parameterRegisters = methodIdItem.getPrototype().getParameterRegisterCount();
283
284        return totalRegisters - parameterRegisters - 1;
285    }
286
287    private boolean isInstanceConstructor() {
288        return (encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) == 0 &&
289               (encodedMethod.accessFlags & AccessFlags.CONSTRUCTOR.getValue()) != 0;
290    }
291
292    private boolean isStaticConstructor() {
293        return (encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) != 0 &&
294               (encodedMethod.accessFlags & AccessFlags.CONSTRUCTOR.getValue()) != 0;
295    }
296
297    public AnalyzedInstruction getStartOfMethod() {
298        return startOfMethod;
299    }
300
301    public AnalyzedInstruction[] makeInstructionArray() {
302        AnalyzedInstruction[] instructionArray = new AnalyzedInstruction[instructions.size()];
303        for (int i=0; i<instructions.size(); i++) {
304            instructionArray[i] = instructions.valueAt(i);
305        }
306        return instructionArray;
307    }
308
309    public ValidationException getValidationException() {
310        return validationException;
311    }
312
313    private static RegisterType[] getParameterTypes(TypeListItem typeListItem, int parameterRegisterCount) {
314        assert typeListItem != null;
315        assert parameterRegisterCount == typeListItem.getRegisterCount();
316
317        RegisterType[] registerTypes = new RegisterType[parameterRegisterCount];
318
319        int registerNum = 0;
320        for (TypeIdItem type: typeListItem.getTypes()) {
321            if (type.getRegisterCount() == 2) {
322                registerTypes[registerNum++] = RegisterType.getWideRegisterTypeForTypeIdItem(type, true);
323                registerTypes[registerNum++] = RegisterType.getWideRegisterTypeForTypeIdItem(type, false);
324            } else {
325                registerTypes[registerNum++] = RegisterType.getRegisterTypeForTypeIdItem(type);
326            }
327        }
328
329        return registerTypes;
330    }
331
332    public int getInstructionAddress(AnalyzedInstruction instruction) {
333        return instructions.keyAt(instruction.instructionIndex);
334    }
335
336    private void setDestinationRegisterTypeAndPropagateChanges(AnalyzedInstruction analyzedInstruction,
337                                                               RegisterType registerType) {
338        setPostRegisterTypeAndPropagateChanges(analyzedInstruction, analyzedInstruction.getDestinationRegister(),
339                registerType);
340    }
341
342
343    private void setAndPropagateDeadness(AnalyzedInstruction analyzedInstruction) {
344        BitSet instructionsToProcess = new BitSet(instructions.size());
345
346        //temporarily set the undeodexeble instruction as dead, so that the "set dead if all predecessors are dead"
347        //operation works
348        analyzedInstruction.dead = true;
349
350        for (AnalyzedInstruction successor: analyzedInstruction.successors) {
351            instructionsToProcess.set(successor.instructionIndex);
352        }
353
354        instructionsToProcess.set(analyzedInstruction.instructionIndex);
355
356        while (!instructionsToProcess.isEmpty()) {
357            for (int i=instructionsToProcess.nextSetBit(0); i>=0; i=instructionsToProcess.nextSetBit(i+1)) {
358                AnalyzedInstruction currentInstruction = instructions.valueAt(i);
359                instructionsToProcess.clear(i);
360
361                if (currentInstruction.dead) {
362                    continue;
363                }
364
365                boolean isDead = true;
366
367                for (AnalyzedInstruction predecessor: currentInstruction.predecessors) {
368                    if (!predecessor.dead) {
369                        isDead = false;
370                        break;
371                    }
372                }
373
374                if (isDead) {
375                    currentInstruction.dead = true;
376
377                    for (AnalyzedInstruction successor: currentInstruction.successors) {
378                        instructionsToProcess.set(successor.instructionIndex);
379                    }
380                }
381            }
382        }
383
384        analyzedInstruction.dead = false;
385    }
386
387    private void setPostRegisterTypeAndPropagateChanges(AnalyzedInstruction analyzedInstruction, int registerNumber,
388                                                RegisterType registerType) {
389
390        BitSet changedInstructions = new BitSet(instructions.size());
391
392        if (!analyzedInstruction.setPostRegisterType(registerNumber, registerType)) {
393            return;
394        }
395
396        propagateRegisterToSuccessors(analyzedInstruction, registerNumber, changedInstructions);
397
398        //Using a for loop inside the while loop optimizes for the common case of the successors of an instruction
399        //occurring after the instruction. Any successors that occur prior to the instruction will be picked up on
400        //the next iteration of the while loop.
401        //This could also be done recursively, but in large methods it would likely cause very deep recursion,
402        //which requires the user to specify a larger stack size. This isn't really a problem, but it is slightly
403        //annoying.
404        while (!changedInstructions.isEmpty()) {
405            for (int instructionIndex=changedInstructions.nextSetBit(0);
406                     instructionIndex>=0;
407                     instructionIndex=changedInstructions.nextSetBit(instructionIndex+1)) {
408
409                changedInstructions.clear(instructionIndex);
410
411                propagateRegisterToSuccessors(instructions.valueAt(instructionIndex), registerNumber,
412                        changedInstructions);
413            }
414        }
415
416        if (registerType.category == RegisterType.Category.LongLo) {
417            checkWidePair(registerNumber, analyzedInstruction);
418            setPostRegisterTypeAndPropagateChanges(analyzedInstruction, registerNumber+1,
419                    RegisterType.getRegisterType(RegisterType.Category.LongHi, null));
420        } else if (registerType.category == RegisterType.Category.DoubleLo) {
421            checkWidePair(registerNumber, analyzedInstruction);
422            setPostRegisterTypeAndPropagateChanges(analyzedInstruction, registerNumber+1,
423                    RegisterType.getRegisterType(RegisterType.Category.DoubleHi, null));
424        }
425    }
426
427    private void propagateRegisterToSuccessors(AnalyzedInstruction instruction, int registerNumber,
428                                               BitSet changedInstructions) {
429        RegisterType postRegisterType = instruction.getPostInstructionRegisterType(registerNumber);
430        for (AnalyzedInstruction successor: instruction.successors) {
431            if (successor.mergeRegister(registerNumber, postRegisterType, verifiedInstructions)) {
432                changedInstructions.set(successor.instructionIndex);
433            }
434        }
435    }
436
437    private void buildInstructionList() {
438        assert encodedMethod != null;
439        assert encodedMethod.codeItem != null;
440        int registerCount = encodedMethod.codeItem.getRegisterCount();
441
442        Instruction[] insns = encodedMethod.codeItem.getInstructions();
443
444        instructions = new SparseArray<AnalyzedInstruction>(insns.length);
445
446        //first, create all the instructions and populate the instructionAddresses array
447        int currentCodeAddress = 0;
448        for (int i=0; i<insns.length; i++) {
449            instructions.append(currentCodeAddress, new AnalyzedInstruction(insns[i], i, registerCount));
450            assert instructions.indexOfKey(currentCodeAddress) == i;
451            currentCodeAddress += insns[i].getSize(currentCodeAddress);
452        }
453
454        //next, populate the exceptionHandlers array. The array item for each instruction that can throw an exception
455        //and is covered by a try block should be set to a list of the first instructions of each exception handler
456        //for the try block covering the instruction
457        CodeItem.TryItem[] tries = encodedMethod.codeItem.getTries();
458        int triesIndex = 0;
459        CodeItem.TryItem currentTry = null;
460        AnalyzedInstruction[] currentExceptionHandlers = null;
461        AnalyzedInstruction[][] exceptionHandlers = new AnalyzedInstruction[insns.length][];
462
463        if (tries != null) {
464            for (int i=0; i<instructions.size(); i++) {
465                AnalyzedInstruction instruction = instructions.valueAt(i);
466                Opcode instructionOpcode = instruction.instruction.opcode;
467                currentCodeAddress = getInstructionAddress(instruction);
468
469                //check if we have gone past the end of the current try
470                if (currentTry != null) {
471                    if (currentTry.getStartCodeAddress() + currentTry.getTryLength() <= currentCodeAddress) {
472                        currentTry = null;
473                        triesIndex++;
474                    }
475                }
476
477                //check if the next try is applicable yet
478                if (currentTry == null && triesIndex < tries.length) {
479                    CodeItem.TryItem tryItem = tries[triesIndex];
480                    if (tryItem.getStartCodeAddress() <= currentCodeAddress) {
481                        assert(tryItem.getStartCodeAddress() + tryItem.getTryLength() > currentCodeAddress);
482
483                        currentTry = tryItem;
484
485                        currentExceptionHandlers = buildExceptionHandlerArray(tryItem);
486                    }
487                }
488
489                //if we're inside a try block, and the instruction can throw an exception, then add the exception handlers
490                //for the current instruction
491                if (currentTry != null && instructionOpcode.canThrow()) {
492                    exceptionHandlers[i] = currentExceptionHandlers;
493                }
494            }
495        }
496
497        //finally, populate the successors and predecessors for each instruction. We start at the fake "StartOfMethod"
498        //instruction and follow the execution path. Any unreachable code won't have any predecessors or successors,
499        //and no reachable code will have an unreachable predessor or successor
500        assert instructions.size() > 0;
501        BitSet instructionsToProcess = new BitSet(insns.length);
502
503        addPredecessorSuccessor(startOfMethod, instructions.valueAt(0), exceptionHandlers, instructionsToProcess);
504        while (!instructionsToProcess.isEmpty()) {
505            int currentInstructionIndex = instructionsToProcess.nextSetBit(0);
506            instructionsToProcess.clear(currentInstructionIndex);
507
508            AnalyzedInstruction instruction = instructions.valueAt(currentInstructionIndex);
509            Opcode instructionOpcode = instruction.instruction.opcode;
510            int instructionCodeAddress = getInstructionAddress(instruction);
511
512            if (instruction.instruction.opcode.canContinue()) {
513                if (instruction.instruction.opcode != Opcode.NOP ||
514                    !instruction.instruction.getFormat().variableSizeFormat) {
515
516                    if (currentInstructionIndex == instructions.size() - 1) {
517                        throw new ValidationException("Execution can continue past the last instruction");
518                    }
519
520                    AnalyzedInstruction nextInstruction = instructions.valueAt(currentInstructionIndex+1);
521                    addPredecessorSuccessor(instruction, nextInstruction, exceptionHandlers, instructionsToProcess);
522                }
523            }
524
525            if (instruction.instruction instanceof OffsetInstruction) {
526                OffsetInstruction offsetInstruction = (OffsetInstruction)instruction.instruction;
527
528                if (instructionOpcode == Opcode.PACKED_SWITCH || instructionOpcode == Opcode.SPARSE_SWITCH) {
529                    MultiOffsetInstruction switchDataInstruction =
530                            (MultiOffsetInstruction)instructions.get(instructionCodeAddress +
531                                    offsetInstruction.getTargetAddressOffset()).instruction;
532                    for (int targetAddressOffset: switchDataInstruction.getTargets()) {
533                        AnalyzedInstruction targetInstruction = instructions.get(instructionCodeAddress +
534                                targetAddressOffset);
535
536                        addPredecessorSuccessor(instruction, targetInstruction, exceptionHandlers,
537                                instructionsToProcess);
538                    }
539                } else {
540                    int targetAddressOffset = offsetInstruction.getTargetAddressOffset();
541                    AnalyzedInstruction targetInstruction = instructions.get(instructionCodeAddress +
542                            targetAddressOffset);
543                    addPredecessorSuccessor(instruction, targetInstruction, exceptionHandlers, instructionsToProcess);
544                }
545            }
546        }
547    }
548
549    private void addPredecessorSuccessor(AnalyzedInstruction predecessor, AnalyzedInstruction successor,
550                                                AnalyzedInstruction[][] exceptionHandlers,
551                                                BitSet instructionsToProcess) {
552        addPredecessorSuccessor(predecessor, successor, exceptionHandlers, instructionsToProcess, false);
553    }
554
555    private void addPredecessorSuccessor(AnalyzedInstruction predecessor, AnalyzedInstruction successor,
556                                                AnalyzedInstruction[][] exceptionHandlers,
557                                                BitSet instructionsToProcess, boolean allowMoveException) {
558
559        if (!allowMoveException && successor.instruction.opcode == Opcode.MOVE_EXCEPTION) {
560            throw new ValidationException("Execution can pass from the " + predecessor.instruction.opcode.name +
561                    " instruction at code address 0x" + Integer.toHexString(getInstructionAddress(predecessor)) +
562                    " to the move-exception instruction at address 0x" +
563                    Integer.toHexString(getInstructionAddress(successor)));
564        }
565
566        if (!successor.addPredecessor(predecessor)) {
567            return;
568        }
569
570        predecessor.addSuccessor(successor);
571        instructionsToProcess.set(successor.getInstructionIndex());
572
573
574        //if the successor can throw an instruction, then we need to add the exception handlers as additional
575        //successors to the predecessor (and then apply this same logic recursively if needed)
576        //Technically, we should handle the monitor-exit instruction as a special case. The exception is actually
577        //thrown *after* the instruction executes, instead of "before" the instruction executes, lke for any other
578        //instruction. But since it doesn't modify any registers, we can treat it like any other instruction.
579        AnalyzedInstruction[] exceptionHandlersForSuccessor = exceptionHandlers[successor.instructionIndex];
580        if (exceptionHandlersForSuccessor != null) {
581            //the item for this instruction in exceptionHandlersForSuccessor should only be set if this instruction
582            //can throw an exception
583            assert successor.instruction.opcode.canThrow();
584
585            for (AnalyzedInstruction exceptionHandler: exceptionHandlersForSuccessor) {
586                addPredecessorSuccessor(predecessor, exceptionHandler, exceptionHandlers, instructionsToProcess, true);
587            }
588        }
589    }
590
591    private AnalyzedInstruction[] buildExceptionHandlerArray(CodeItem.TryItem tryItem) {
592        int exceptionHandlerCount = tryItem.encodedCatchHandler.handlers.length;
593        int catchAllHandler = tryItem.encodedCatchHandler.getCatchAllHandlerAddress();
594        if (catchAllHandler != -1) {
595            exceptionHandlerCount++;
596        }
597
598        AnalyzedInstruction[] exceptionHandlers = new AnalyzedInstruction[exceptionHandlerCount];
599        for (int i=0; i<tryItem.encodedCatchHandler.handlers.length; i++) {
600            exceptionHandlers[i] = instructions.get(tryItem.encodedCatchHandler.handlers[i].getHandlerAddress());
601        }
602
603        if (catchAllHandler != -1) {
604            exceptionHandlers[exceptionHandlers.length - 1] = instructions.get(catchAllHandler);
605        }
606
607        return exceptionHandlers;
608    }
609
610    private boolean analyzeInstruction(AnalyzedInstruction analyzedInstruction) {
611        Instruction instruction = analyzedInstruction.instruction;
612
613        switch (instruction.opcode) {
614            case NOP:
615                return true;
616            case MOVE:
617            case MOVE_FROM16:
618            case MOVE_16:
619                handleMove(analyzedInstruction, Primitive32BitCategories);
620                return true;
621            case MOVE_WIDE:
622            case MOVE_WIDE_FROM16:
623            case MOVE_WIDE_16:
624                handleMove(analyzedInstruction, WideLowCategories);
625                return true;
626            case MOVE_OBJECT:
627            case MOVE_OBJECT_FROM16:
628            case MOVE_OBJECT_16:
629                handleMove(analyzedInstruction, ReferenceOrUninitCategories);
630                return true;
631            case MOVE_RESULT:
632                handleMoveResult(analyzedInstruction, Primitive32BitCategories);
633                return true;
634            case MOVE_RESULT_WIDE:
635                handleMoveResult(analyzedInstruction, WideLowCategories);
636                return true;
637            case MOVE_RESULT_OBJECT:
638                handleMoveResult(analyzedInstruction, ReferenceCategories);
639                return true;
640            case MOVE_EXCEPTION:
641                handleMoveException(analyzedInstruction);
642                return true;
643            case RETURN_VOID:
644                handleReturnVoid(analyzedInstruction);
645                return true;
646            case RETURN:
647                handleReturn(analyzedInstruction, Primitive32BitCategories);
648                return true;
649            case RETURN_WIDE:
650                handleReturn(analyzedInstruction, WideLowCategories);
651                return true;
652            case RETURN_OBJECT:
653                handleReturn(analyzedInstruction, ReferenceCategories);
654                return true;
655            case CONST_4:
656            case CONST_16:
657            case CONST:
658                handleConst(analyzedInstruction);
659                return true;
660            case CONST_HIGH16:
661                handleConstHigh16(analyzedInstruction);
662                return true;
663            case CONST_WIDE_16:
664            case CONST_WIDE_32:
665            case CONST_WIDE:
666            case CONST_WIDE_HIGH16:
667                handleWideConst(analyzedInstruction);
668                return true;
669            case CONST_STRING:
670            case CONST_STRING_JUMBO:
671                handleConstString(analyzedInstruction);
672                return true;
673            case CONST_CLASS:
674                handleConstClass(analyzedInstruction);
675                return true;
676            case MONITOR_ENTER:
677            case MONITOR_EXIT:
678                handleMonitor(analyzedInstruction);
679                return true;
680            case CHECK_CAST:
681                handleCheckCast(analyzedInstruction);
682                return true;
683            case INSTANCE_OF:
684                handleInstanceOf(analyzedInstruction);
685                return true;
686            case ARRAY_LENGTH:
687                handleArrayLength(analyzedInstruction);
688                return true;
689            case NEW_INSTANCE:
690                handleNewInstance(analyzedInstruction);
691                return true;
692            case NEW_ARRAY:
693                handleNewArray(analyzedInstruction);
694                return true;
695            case FILLED_NEW_ARRAY:
696                handleFilledNewArray(analyzedInstruction);
697                return true;
698            case FILLED_NEW_ARRAY_RANGE:
699                handleFilledNewArrayRange(analyzedInstruction);
700                return true;
701            case FILL_ARRAY_DATA:
702                handleFillArrayData(analyzedInstruction);
703                return true;
704            case THROW:
705                handleThrow(analyzedInstruction);
706                return true;
707            case GOTO:
708            case GOTO_16:
709            case GOTO_32:
710                //nothing to do
711                return true;
712            case PACKED_SWITCH:
713                handleSwitch(analyzedInstruction, Format.PackedSwitchData);
714                return true;
715            case SPARSE_SWITCH:
716                handleSwitch(analyzedInstruction, Format.SparseSwitchData);
717                return true;
718            case CMPL_FLOAT:
719            case CMPG_FLOAT:
720                handleFloatWideCmp(analyzedInstruction, Primitive32BitCategories);
721                return true;
722            case CMPL_DOUBLE:
723            case CMPG_DOUBLE:
724            case CMP_LONG:
725                handleFloatWideCmp(analyzedInstruction, WideLowCategories);
726                return true;
727            case IF_EQ:
728            case IF_NE:
729                handleIfEqNe(analyzedInstruction);
730                return true;
731            case IF_LT:
732            case IF_GE:
733            case IF_GT:
734            case IF_LE:
735                handleIf(analyzedInstruction);
736                return true;
737            case IF_EQZ:
738            case IF_NEZ:
739                handleIfEqzNez(analyzedInstruction);
740                return true;
741            case IF_LTZ:
742            case IF_GEZ:
743            case IF_GTZ:
744            case IF_LEZ:
745                handleIfz(analyzedInstruction);
746                return true;
747            case AGET:
748                handle32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Integer);
749                return true;
750            case AGET_BOOLEAN:
751                handle32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Boolean);
752                return true;
753            case AGET_BYTE:
754                handle32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Byte);
755                return true;
756            case AGET_CHAR:
757                handle32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Char);
758                return true;
759            case AGET_SHORT:
760                handle32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Short);
761                return true;
762            case AGET_WIDE:
763                handleAgetWide(analyzedInstruction);
764                return true;
765            case AGET_OBJECT:
766                handleAgetObject(analyzedInstruction);
767                return true;
768            case APUT:
769                handle32BitPrimitiveAput(analyzedInstruction, RegisterType.Category.Integer);
770                return true;
771            case APUT_BOOLEAN:
772                handle32BitPrimitiveAput(analyzedInstruction, RegisterType.Category.Boolean);
773                return true;
774            case APUT_BYTE:
775                handle32BitPrimitiveAput(analyzedInstruction, RegisterType.Category.Byte);
776                return true;
777            case APUT_CHAR:
778                handle32BitPrimitiveAput(analyzedInstruction, RegisterType.Category.Char);
779                return true;
780            case APUT_SHORT:
781                handle32BitPrimitiveAput(analyzedInstruction, RegisterType.Category.Short);
782                return true;
783            case APUT_WIDE:
784                handleAputWide(analyzedInstruction);
785                return true;
786            case APUT_OBJECT:
787                handleAputObject(analyzedInstruction);
788                return true;
789            case IGET:
790                handle32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Integer);
791                return true;
792            case IGET_BOOLEAN:
793                handle32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Boolean);
794                return true;
795            case IGET_BYTE:
796                handle32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Byte);
797                return true;
798            case IGET_CHAR:
799                handle32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Char);
800                return true;
801            case IGET_SHORT:
802                handle32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Short);
803                return true;
804            case IGET_WIDE:
805                handleIgetWide(analyzedInstruction);
806                return true;
807            case IGET_OBJECT:
808                handleIgetObject(analyzedInstruction);
809                return true;
810            case IPUT:
811                handle32BitPrimitiveIput(analyzedInstruction, RegisterType.Category.Integer);
812                return true;
813            case IPUT_BOOLEAN:
814                handle32BitPrimitiveIput(analyzedInstruction, RegisterType.Category.Boolean);
815                return true;
816            case IPUT_BYTE:
817                handle32BitPrimitiveIput(analyzedInstruction, RegisterType.Category.Byte);
818                return true;
819            case IPUT_CHAR:
820                handle32BitPrimitiveIput(analyzedInstruction, RegisterType.Category.Char);
821                return true;
822            case IPUT_SHORT:
823                handle32BitPrimitiveIput(analyzedInstruction, RegisterType.Category.Short);
824                return true;
825            case IPUT_WIDE:
826                handleIputWide(analyzedInstruction);
827                return true;
828            case IPUT_OBJECT:
829                handleIputObject(analyzedInstruction);
830                return true;
831            case SGET:
832                handle32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Integer);
833                return true;
834            case SGET_BOOLEAN:
835                handle32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Boolean);
836                return true;
837            case SGET_BYTE:
838                handle32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Byte);
839                return true;
840            case SGET_CHAR:
841                handle32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Char);
842                return true;
843            case SGET_SHORT:
844                handle32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Short);
845                return true;
846            case SGET_WIDE:
847                handleSgetWide(analyzedInstruction);
848                return true;
849            case SGET_OBJECT:
850                handleSgetObject(analyzedInstruction);
851                return true;
852            case SPUT:
853                handle32BitPrimitiveSput(analyzedInstruction, RegisterType.Category.Integer);
854                return true;
855            case SPUT_BOOLEAN:
856                handle32BitPrimitiveSput(analyzedInstruction, RegisterType.Category.Boolean);
857                return true;
858            case SPUT_BYTE:
859                handle32BitPrimitiveSput(analyzedInstruction, RegisterType.Category.Byte);
860                return true;
861            case SPUT_CHAR:
862                handle32BitPrimitiveSput(analyzedInstruction, RegisterType.Category.Char);
863                return true;
864            case SPUT_SHORT:
865                handle32BitPrimitiveSput(analyzedInstruction, RegisterType.Category.Short);
866                return true;
867            case SPUT_WIDE:
868                handleSputWide(analyzedInstruction);
869                return true;
870            case SPUT_OBJECT:
871                handleSputObject(analyzedInstruction);
872                return true;
873            case INVOKE_VIRTUAL:
874                handleInvoke(analyzedInstruction, INVOKE_VIRTUAL);
875                return true;
876            case INVOKE_SUPER:
877                handleInvoke(analyzedInstruction, INVOKE_SUPER);
878                return true;
879            case INVOKE_DIRECT:
880                handleInvoke(analyzedInstruction, INVOKE_DIRECT);
881                return true;
882            case INVOKE_STATIC:
883                handleInvoke(analyzedInstruction, INVOKE_STATIC);
884                return true;
885            case INVOKE_INTERFACE:
886                handleInvoke(analyzedInstruction, INVOKE_INTERFACE);
887                return true;
888            case INVOKE_VIRTUAL_RANGE:
889                handleInvokeRange(analyzedInstruction, INVOKE_VIRTUAL);
890                return true;
891            case INVOKE_SUPER_RANGE:
892                handleInvokeRange(analyzedInstruction, INVOKE_SUPER);
893                return true;
894            case INVOKE_DIRECT_RANGE:
895                handleInvokeRange(analyzedInstruction, INVOKE_DIRECT);
896                return true;
897            case INVOKE_STATIC_RANGE:
898                handleInvokeRange(analyzedInstruction, INVOKE_STATIC);
899                return true;
900            case INVOKE_INTERFACE_RANGE:
901                handleInvokeRange(analyzedInstruction, INVOKE_INTERFACE);
902                return true;
903            case NEG_INT:
904            case NOT_INT:
905                handleUnaryOp(analyzedInstruction, Primitive32BitCategories, RegisterType.Category.Integer);
906                return true;
907            case NEG_LONG:
908            case NOT_LONG:
909                handleUnaryOp(analyzedInstruction, WideLowCategories, RegisterType.Category.LongLo);
910                return true;
911            case NEG_FLOAT:
912                handleUnaryOp(analyzedInstruction, Primitive32BitCategories, RegisterType.Category.Float);
913                return true;
914            case NEG_DOUBLE:
915                handleUnaryOp(analyzedInstruction, WideLowCategories, RegisterType.Category.DoubleLo);
916                return true;
917            case INT_TO_LONG:
918                handleUnaryOp(analyzedInstruction, Primitive32BitCategories, RegisterType.Category.LongLo);
919                return true;
920            case INT_TO_FLOAT:
921                handleUnaryOp(analyzedInstruction, Primitive32BitCategories, RegisterType.Category.Float);
922                return true;
923            case INT_TO_DOUBLE:
924                handleUnaryOp(analyzedInstruction, Primitive32BitCategories, RegisterType.Category.DoubleLo);
925                return true;
926            case LONG_TO_INT:
927            case DOUBLE_TO_INT:
928                handleUnaryOp(analyzedInstruction, WideLowCategories, RegisterType.Category.Integer);
929                return true;
930            case LONG_TO_FLOAT:
931            case DOUBLE_TO_FLOAT:
932                handleUnaryOp(analyzedInstruction, WideLowCategories, RegisterType.Category.Float);
933                return true;
934            case LONG_TO_DOUBLE:
935                handleUnaryOp(analyzedInstruction, WideLowCategories, RegisterType.Category.DoubleLo);
936                return true;
937            case FLOAT_TO_INT:
938                handleUnaryOp(analyzedInstruction, Primitive32BitCategories, RegisterType.Category.Integer);
939                return true;
940            case FLOAT_TO_LONG:
941                handleUnaryOp(analyzedInstruction, Primitive32BitCategories, RegisterType.Category.LongLo);
942                return true;
943            case FLOAT_TO_DOUBLE:
944                handleUnaryOp(analyzedInstruction, Primitive32BitCategories, RegisterType.Category.DoubleLo);
945                return true;
946            case DOUBLE_TO_LONG:
947                handleUnaryOp(analyzedInstruction, WideLowCategories, RegisterType.Category.LongLo);
948                return true;
949            case INT_TO_BYTE:
950                handleUnaryOp(analyzedInstruction, Primitive32BitCategories, RegisterType.Category.Byte);
951                return true;
952            case INT_TO_CHAR:
953                handleUnaryOp(analyzedInstruction, Primitive32BitCategories, RegisterType.Category.Char);
954                return true;
955            case INT_TO_SHORT:
956                handleUnaryOp(analyzedInstruction, Primitive32BitCategories, RegisterType.Category.Short);
957                return true;
958            case ADD_INT:
959            case SUB_INT:
960            case MUL_INT:
961            case DIV_INT:
962            case REM_INT:
963            case SHL_INT:
964            case SHR_INT:
965            case USHR_INT:
966                handleBinaryOp(analyzedInstruction, Primitive32BitCategories, Primitive32BitCategories,
967                        RegisterType.Category.Integer, false);
968                return true;
969            case AND_INT:
970            case OR_INT:
971            case XOR_INT:
972                handleBinaryOp(analyzedInstruction, Primitive32BitCategories, Primitive32BitCategories,
973                        RegisterType.Category.Integer, true);
974                return true;
975            case ADD_LONG:
976            case SUB_LONG:
977            case MUL_LONG:
978            case DIV_LONG:
979            case REM_LONG:
980            case AND_LONG:
981            case OR_LONG:
982            case XOR_LONG:
983                handleBinaryOp(analyzedInstruction, WideLowCategories, WideLowCategories, RegisterType.Category.LongLo,
984                        false);
985                return true;
986            case SHL_LONG:
987            case SHR_LONG:
988            case USHR_LONG:
989                handleBinaryOp(analyzedInstruction, WideLowCategories, Primitive32BitCategories,
990                        RegisterType.Category.LongLo, false);
991                return true;
992            case ADD_FLOAT:
993            case SUB_FLOAT:
994            case MUL_FLOAT:
995            case DIV_FLOAT:
996            case REM_FLOAT:
997                handleBinaryOp(analyzedInstruction, Primitive32BitCategories, Primitive32BitCategories,
998                        RegisterType.Category.Float, false);
999                return true;
1000            case ADD_DOUBLE:
1001            case SUB_DOUBLE:
1002            case MUL_DOUBLE:
1003            case DIV_DOUBLE:
1004            case REM_DOUBLE:
1005                handleBinaryOp(analyzedInstruction, WideLowCategories, WideLowCategories,
1006                        RegisterType.Category.DoubleLo, false);
1007                return true;
1008            case ADD_INT_2ADDR:
1009            case SUB_INT_2ADDR:
1010            case MUL_INT_2ADDR:
1011            case DIV_INT_2ADDR:
1012            case REM_INT_2ADDR:
1013            case SHL_INT_2ADDR:
1014            case SHR_INT_2ADDR:
1015            case USHR_INT_2ADDR:
1016                handleBinary2AddrOp(analyzedInstruction, Primitive32BitCategories, Primitive32BitCategories,
1017                        RegisterType.Category.Integer, false);
1018                return true;
1019            case AND_INT_2ADDR:
1020            case OR_INT_2ADDR:
1021            case XOR_INT_2ADDR:
1022                handleBinary2AddrOp(analyzedInstruction, Primitive32BitCategories, Primitive32BitCategories,
1023                        RegisterType.Category.Integer, true);
1024                return true;
1025            case ADD_LONG_2ADDR:
1026            case SUB_LONG_2ADDR:
1027            case MUL_LONG_2ADDR:
1028            case DIV_LONG_2ADDR:
1029            case REM_LONG_2ADDR:
1030            case AND_LONG_2ADDR:
1031            case OR_LONG_2ADDR:
1032            case XOR_LONG_2ADDR:
1033                handleBinary2AddrOp(analyzedInstruction, WideLowCategories, WideLowCategories,
1034                        RegisterType.Category.LongLo, false);
1035                return true;
1036            case SHL_LONG_2ADDR:
1037            case SHR_LONG_2ADDR:
1038            case USHR_LONG_2ADDR:
1039                handleBinary2AddrOp(analyzedInstruction, WideLowCategories, Primitive32BitCategories,
1040                        RegisterType.Category.LongLo, false);
1041                return true;
1042            case ADD_FLOAT_2ADDR:
1043            case SUB_FLOAT_2ADDR:
1044            case MUL_FLOAT_2ADDR:
1045            case DIV_FLOAT_2ADDR:
1046            case REM_FLOAT_2ADDR:
1047                handleBinary2AddrOp(analyzedInstruction, Primitive32BitCategories, Primitive32BitCategories,
1048                        RegisterType.Category.Float, false);
1049                return true;
1050            case ADD_DOUBLE_2ADDR:
1051            case SUB_DOUBLE_2ADDR:
1052            case MUL_DOUBLE_2ADDR:
1053            case DIV_DOUBLE_2ADDR:
1054            case REM_DOUBLE_2ADDR:
1055                handleBinary2AddrOp(analyzedInstruction, WideLowCategories, WideLowCategories,
1056                        RegisterType.Category.DoubleLo, false);
1057                return true;
1058            case ADD_INT_LIT16:
1059            case RSUB_INT:
1060            case MUL_INT_LIT16:
1061            case DIV_INT_LIT16:
1062            case REM_INT_LIT16:
1063                handleLiteralBinaryOp(analyzedInstruction, Primitive32BitCategories, RegisterType.Category.Integer,
1064                        false);
1065                return true;
1066            case AND_INT_LIT16:
1067            case OR_INT_LIT16:
1068            case XOR_INT_LIT16:
1069                handleLiteralBinaryOp(analyzedInstruction, Primitive32BitCategories, RegisterType.Category.Integer,
1070                        true);
1071                return true;
1072            case ADD_INT_LIT8:
1073            case RSUB_INT_LIT8:
1074            case MUL_INT_LIT8:
1075            case DIV_INT_LIT8:
1076            case REM_INT_LIT8:
1077            case SHL_INT_LIT8:
1078                handleLiteralBinaryOp(analyzedInstruction, Primitive32BitCategories, RegisterType.Category.Integer,
1079                        false);
1080                return true;
1081            case AND_INT_LIT8:
1082            case OR_INT_LIT8:
1083            case XOR_INT_LIT8:
1084                handleLiteralBinaryOp(analyzedInstruction, Primitive32BitCategories, RegisterType.Category.Integer,
1085                        true);
1086                return true;
1087            case SHR_INT_LIT8:
1088                handleLiteralBinaryOp(analyzedInstruction, Primitive32BitCategories,
1089                        getDestTypeForLiteralShiftRight(analyzedInstruction, true), false);
1090                return true;
1091            case USHR_INT_LIT8:
1092                handleLiteralBinaryOp(analyzedInstruction, Primitive32BitCategories,
1093                        getDestTypeForLiteralShiftRight(analyzedInstruction, false), false);
1094                return true;
1095            case EXECUTE_INLINE:
1096                handleExecuteInline(analyzedInstruction);
1097                return true;
1098            case EXECUTE_INLINE_RANGE:
1099                handleExecuteInlineRange(analyzedInstruction);
1100                return true;
1101            case INVOKE_DIRECT_EMPTY:
1102                handleInvokeDirectEmpty(analyzedInstruction);
1103                return true;
1104            case IGET_QUICK:
1105            case IGET_WIDE_QUICK:
1106            case IGET_OBJECT_QUICK:
1107                return handleIputIgetQuick(analyzedInstruction, false);
1108            case IPUT_QUICK:
1109            case IPUT_WIDE_QUICK:
1110            case IPUT_OBJECT_QUICK:
1111                return handleIputIgetQuick(analyzedInstruction, true);
1112            case INVOKE_VIRTUAL_QUICK:
1113                return handleInvokeVirtualQuick(analyzedInstruction, false, false);
1114            case INVOKE_SUPER_QUICK:
1115                return handleInvokeVirtualQuick(analyzedInstruction, true, false);
1116            case INVOKE_VIRTUAL_QUICK_RANGE:
1117                return handleInvokeVirtualQuick(analyzedInstruction, false, true);
1118            case INVOKE_SUPER_QUICK_RANGE:
1119                return handleInvokeVirtualQuick(analyzedInstruction, true, true);
1120            default:
1121                assert false;
1122                return true;
1123        }
1124    }
1125
1126    private static final EnumSet<RegisterType.Category> Primitive32BitCategories = EnumSet.of(
1127            RegisterType.Category.Null,
1128            RegisterType.Category.One,
1129            RegisterType.Category.Boolean,
1130            RegisterType.Category.Byte,
1131            RegisterType.Category.PosByte,
1132            RegisterType.Category.Short,
1133            RegisterType.Category.PosShort,
1134            RegisterType.Category.Char,
1135            RegisterType.Category.Integer,
1136            RegisterType.Category.Float);
1137
1138    private static final EnumSet<RegisterType.Category> WideLowCategories = EnumSet.of(
1139            RegisterType.Category.LongLo,
1140            RegisterType.Category.DoubleLo);
1141
1142    private static final EnumSet<RegisterType.Category> WideHighCategories = EnumSet.of(
1143            RegisterType.Category.LongHi,
1144            RegisterType.Category.DoubleHi);
1145
1146    private static final EnumSet<RegisterType.Category> ReferenceCategories = EnumSet.of(
1147            RegisterType.Category.Null,
1148            RegisterType.Category.Reference);
1149
1150    private static final EnumSet<RegisterType.Category> ReferenceOrUninitThisCategories = EnumSet.of(
1151            RegisterType.Category.Null,
1152            RegisterType.Category.UninitThis,
1153            RegisterType.Category.Reference);
1154
1155    private static final EnumSet<RegisterType.Category> ReferenceOrUninitCategories = EnumSet.of(
1156            RegisterType.Category.Null,
1157            RegisterType.Category.UninitRef,
1158            RegisterType.Category.UninitThis,
1159            RegisterType.Category.Reference);
1160
1161    private static final EnumSet<RegisterType.Category> ReferenceAndPrimitive32BitCategories = EnumSet.of(
1162            RegisterType.Category.Null,
1163            RegisterType.Category.One,
1164            RegisterType.Category.Boolean,
1165            RegisterType.Category.Byte,
1166            RegisterType.Category.PosByte,
1167            RegisterType.Category.Short,
1168            RegisterType.Category.PosShort,
1169            RegisterType.Category.Char,
1170            RegisterType.Category.Integer,
1171            RegisterType.Category.Float,
1172            RegisterType.Category.Reference);
1173
1174    private static final EnumSet<RegisterType.Category> BooleanCategories = EnumSet.of(
1175            RegisterType.Category.Null,
1176            RegisterType.Category.One,
1177            RegisterType.Category.Boolean);
1178
1179    private void handleMove(AnalyzedInstruction analyzedInstruction, EnumSet validCategories) {
1180        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
1181
1182        RegisterType sourceRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
1183                validCategories);
1184
1185        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, sourceRegisterType);
1186    }
1187
1188    private void handleMoveResult(AnalyzedInstruction analyzedInstruction,
1189                                     EnumSet<RegisterType.Category> allowedCategories) {
1190
1191        //TODO: handle the case when the previous instruction is an odexed instruction
1192
1193        if (analyzedInstruction.instructionIndex == 0) {
1194            throw new ValidationException(analyzedInstruction.instruction.opcode.name + " cannot be the first " +
1195                    "instruction in a method. It must occur after an invoke-*/fill-new-array instruction");
1196        }
1197
1198        AnalyzedInstruction previousInstruction = instructions.valueAt(analyzedInstruction.instructionIndex-1);
1199
1200        if (!previousInstruction.instruction.opcode.setsResult()) {
1201            throw new ValidationException(analyzedInstruction.instruction.opcode.name + " must occur after an " +
1202                    "invoke-*/fill-new-array instruction");
1203        }
1204
1205        //TODO: does dalvik allow a move-result after an invoke with a void return type?
1206        RegisterType resultRegisterType;
1207
1208        InstructionWithReference invokeInstruction = (InstructionWithReference)previousInstruction.instruction;
1209        Item item = invokeInstruction.getReferencedItem();
1210
1211        if (item instanceof MethodIdItem) {
1212            resultRegisterType = RegisterType.getRegisterTypeForTypeIdItem(
1213                    ((MethodIdItem)item).getPrototype().getReturnType());
1214        } else {
1215            assert item instanceof TypeIdItem;
1216            resultRegisterType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item);
1217        }
1218
1219        if (!allowedCategories.contains(resultRegisterType.category)) {
1220            throw new ValidationException(String.format("Wrong move-result* instruction for return value %s",
1221                    resultRegisterType.toString()));
1222        }
1223
1224        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, resultRegisterType);
1225    }
1226
1227    private void handleMoveException(AnalyzedInstruction analyzedInstruction) {
1228        CodeItem.TryItem[] tries = encodedMethod.codeItem.getTries();
1229        int instructionAddress = getInstructionAddress(analyzedInstruction);
1230
1231        if (tries == null) {
1232            throw new ValidationException("move-exception must be the first instruction in an exception handler block");
1233        }
1234
1235        RegisterType exceptionType = null;
1236
1237        for (CodeItem.TryItem tryItem: encodedMethod.codeItem.getTries()) {
1238            if (tryItem.encodedCatchHandler.getCatchAllHandlerAddress() == instructionAddress) {
1239                exceptionType = RegisterType.getRegisterType(RegisterType.Category.Reference,
1240                        ClassPath.getClassDef("Ljava/lang/Throwable;"));
1241                break;
1242            }
1243            for (CodeItem.EncodedTypeAddrPair handler: tryItem.encodedCatchHandler.handlers) {
1244                if (handler.getHandlerAddress() == instructionAddress) {
1245                    exceptionType = RegisterType.getRegisterTypeForTypeIdItem(handler.exceptionType)
1246                            .merge(exceptionType);
1247                }
1248            }
1249        }
1250
1251        if (exceptionType == null) {
1252            throw new ValidationException("move-exception must be the first instruction in an exception handler block");
1253        }
1254
1255        //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)
1256        if (exceptionType.category != RegisterType.Category.Reference) {
1257            throw new ValidationException(String.format("Exception type %s is not a reference type",
1258                    exceptionType.toString()));
1259        }
1260
1261        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, exceptionType);
1262    }
1263
1264    private void handleReturnVoid(AnalyzedInstruction analyzedInstruction) {
1265        TypeIdItem returnType = encodedMethod.method.getPrototype().getReturnType();
1266        if (returnType.getTypeDescriptor().charAt(0) != 'V') {
1267            //TODO: could add which return-* variation should be used instead
1268            throw new ValidationException("Cannot use return-void with a non-void return type (" +
1269                returnType.getTypeDescriptor() + ")");
1270        }
1271    }
1272
1273    private void handleReturn(AnalyzedInstruction analyzedInstruction, EnumSet validCategories) {
1274        /*if (this.isInstanceConstructor()) {
1275            checkConstructorReturn(analyzedInstruction);
1276        }*/
1277
1278        SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
1279        int returnRegister = instruction.getRegisterA();
1280        RegisterType returnRegisterType = getAndCheckSourceRegister(analyzedInstruction, returnRegister,
1281                validCategories);
1282
1283        TypeIdItem returnType = encodedMethod.method.getPrototype().getReturnType();
1284        if (returnType.getTypeDescriptor().charAt(0) == 'V') {
1285            throw new ValidationException("Cannot use return with a void return type. Use return-void instead");
1286        }
1287
1288        RegisterType methodReturnRegisterType = RegisterType.getRegisterTypeForTypeIdItem(returnType);
1289
1290        if (!validCategories.contains(methodReturnRegisterType.category)) {
1291            //TODO: could add which return-* variation should be used instead
1292            throw new ValidationException(String.format("Cannot use %s with return type %s",
1293                    analyzedInstruction.instruction.opcode.name, returnType.getTypeDescriptor()));
1294        }
1295
1296        if (validCategories == ReferenceCategories) {
1297            if (methodReturnRegisterType.type.isInterface()) {
1298                if (returnRegisterType.category != RegisterType.Category.Null &&
1299                    !returnRegisterType.type.implementsInterface(methodReturnRegisterType.type)) {
1300                    //TODO: how to handle warnings?
1301                }
1302            } else {
1303                if (returnRegisterType.category == RegisterType.Category.Reference &&
1304                    !returnRegisterType.type.extendsClass(methodReturnRegisterType.type)) {
1305
1306                    throw new ValidationException(String.format("The return value in register v%d (%s) is not " +
1307                            "compatible with the method's return type %s", returnRegister,
1308                            returnRegisterType.type.getClassType(), methodReturnRegisterType.type.getClassType()));
1309                }
1310            }
1311        }
1312    }
1313
1314    private void handleConst(AnalyzedInstruction analyzedInstruction) {
1315        LiteralInstruction instruction = (LiteralInstruction)analyzedInstruction.instruction;
1316
1317        RegisterType newDestinationRegisterType = RegisterType.getRegisterTypeForLiteral(instruction.getLiteral());
1318
1319        //we assume that the literal value is a valid value for the given instruction type, because it's impossible
1320        //to store an invalid literal with the instruction. so we don't need to check the type of the literal
1321        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, newDestinationRegisterType);
1322    }
1323
1324    private void handleConstHigh16(AnalyzedInstruction analyzedInstruction) {
1325        LiteralInstruction instruction = (LiteralInstruction)analyzedInstruction.instruction;
1326
1327        long literalValue = instruction.getLiteral() << 16;
1328        RegisterType newDestinationRegisterType = RegisterType.getRegisterTypeForLiteral(literalValue);
1329
1330        //we assume that the literal value is a valid value for the given instruction type, because it's impossible
1331        //to store an invalid literal with the instruction. so we don't need to check the type of the literal
1332        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, newDestinationRegisterType);
1333    }
1334
1335    private void handleWideConst(AnalyzedInstruction analyzedInstruction) {
1336        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1337                RegisterType.getRegisterType(RegisterType.Category.LongLo, null));
1338    }
1339
1340    private void handleConstString(AnalyzedInstruction analyzedInstruction) {
1341        ClassPath.ClassDef stringClassDef = ClassPath.getClassDef("Ljava/lang/String;");
1342        RegisterType stringType = RegisterType.getRegisterType(RegisterType.Category.Reference, stringClassDef);
1343        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, stringType);
1344    }
1345
1346    private void handleConstClass(AnalyzedInstruction analyzedInstruction) {
1347        ClassPath.ClassDef classClassDef = ClassPath.getClassDef("Ljava/lang/Class;");
1348        RegisterType classType = RegisterType.getRegisterType(RegisterType.Category.Reference, classClassDef);
1349
1350        InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
1351        Item item = instruction.getReferencedItem();
1352        assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
1353
1354        //TODO: need to check class access
1355        //make sure the referenced class is resolvable
1356        ClassPath.getClassDef((TypeIdItem)item);
1357
1358        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, classType);
1359    }
1360
1361    private void handleMonitor(AnalyzedInstruction analyzedInstruction) {
1362        SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
1363        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), ReferenceCategories);
1364    }
1365
1366    private void handleCheckCast(AnalyzedInstruction analyzedInstruction) {
1367        {
1368            //ensure the "source" register is a reference type
1369            SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
1370
1371            RegisterType registerType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(),
1372                    ReferenceCategories);
1373        }
1374
1375        {
1376            //resolve and verify the class that we're casting to
1377            InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
1378
1379            Item item = instruction.getReferencedItem();
1380            assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
1381
1382            //TODO: need to check class access
1383            RegisterType castRegisterType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item);
1384            if (castRegisterType.category != RegisterType.Category.Reference) {
1385                //TODO: verify that dalvik allows a non-reference type..
1386                //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)
1387            }
1388
1389            setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, castRegisterType);
1390        }
1391    }
1392
1393    private void handleInstanceOf(AnalyzedInstruction analyzedInstruction) {
1394        {
1395            //ensure the register that is being checks is a reference type
1396            TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
1397
1398            getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), ReferenceCategories);
1399        }
1400
1401        {
1402            //resolve and verify the class that we're checking against
1403            InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
1404
1405            Item item = instruction.getReferencedItem();
1406            assert  item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
1407            RegisterType registerType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item);
1408            if (registerType.category != RegisterType.Category.Reference) {
1409                throw new ValidationException(String.format("Cannot use instance-of with a non-reference type %s",
1410                        registerType.toString()));
1411            }
1412
1413            //TODO: is it valid to use an array type?
1414
1415            //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.
1416            setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1417                    RegisterType.getRegisterType(RegisterType.Category.Boolean, null));
1418        }
1419    }
1420
1421    private void handleArrayLength(AnalyzedInstruction analyzedInstruction) {
1422        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
1423
1424        int arrayRegisterNumber = instruction.getRegisterB();
1425        RegisterType arrayRegisterType = getAndCheckSourceRegister(analyzedInstruction, arrayRegisterNumber,
1426                ReferenceCategories);
1427
1428        if (arrayRegisterType.type != null) {
1429            if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
1430                throw new ValidationException(String.format("Cannot use array-length with non-array type %s",
1431                        arrayRegisterType.type.getClassType()));
1432            }
1433            assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
1434        }
1435
1436        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1437                RegisterType.getRegisterType(RegisterType.Category.Integer, null));
1438    }
1439
1440    private void handleNewInstance(AnalyzedInstruction analyzedInstruction) {
1441        InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
1442
1443        int register = ((SingleRegisterInstruction)analyzedInstruction.instruction).getRegisterA();
1444        RegisterType destRegisterType = analyzedInstruction.postRegisterMap[register];
1445        if (destRegisterType.category != RegisterType.Category.Unknown) {
1446            assert destRegisterType.category == RegisterType.Category.UninitRef;
1447
1448            //the "post-instruction" destination register will only be set if we've gone over
1449            //this instruction at least once before. If this is the case, then we need to check
1450            //all the other registers, and make sure that none of them contain the same
1451            //uninitialized reference that is in the destination register.
1452
1453            for (int i=0; i<analyzedInstruction.postRegisterMap.length; i++) {
1454                if (i==register) {
1455                    continue;
1456                }
1457
1458                if (analyzedInstruction.getPreInstructionRegisterType(i) == destRegisterType) {
1459                    throw new ValidationException(String.format("Register v%d contains an uninitialized reference " +
1460                            "that was created by this new-instance instruction.", i));
1461                }
1462            }
1463
1464            return;
1465        }
1466
1467        Item item = instruction.getReferencedItem();
1468        assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
1469
1470        //TODO: need to check class access
1471        RegisterType classType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item);
1472        if (classType.category != RegisterType.Category.Reference) {
1473            throw new ValidationException(String.format("Cannot use new-instance with a non-reference type %s",
1474                    classType.toString()));
1475        }
1476
1477        if (((TypeIdItem)item).getTypeDescriptor().charAt(0) == '[') {
1478            throw new ValidationException("Cannot use array type \"" + ((TypeIdItem)item).getTypeDescriptor() +
1479                    "\" with new-instance. Use new-array instead.");
1480        }
1481
1482        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1483                RegisterType.getUnitializedReference(classType.type));
1484    }
1485
1486    private void handleNewArray(AnalyzedInstruction analyzedInstruction) {
1487        {
1488            TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
1489            getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), Primitive32BitCategories);
1490        }
1491
1492        InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
1493
1494        Item item = instruction.getReferencedItem();
1495        assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
1496
1497        RegisterType arrayType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item);
1498        assert arrayType.type instanceof ClassPath.ArrayClassDef;
1499
1500        if (arrayType.category != RegisterType.Category.Reference) {
1501            throw new ValidationException(String.format("Cannot use new-array with a non-reference type %s",
1502                    arrayType.toString()));
1503        }
1504        if (arrayType.type.getClassType().charAt(0) != '[') {
1505            throw new ValidationException("Cannot use non-array type \"" + arrayType.type.getClassType() +
1506                    "\" with new-array. Use new-instance instead.");
1507        }
1508
1509        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, arrayType);
1510    }
1511
1512    private static interface RegisterIterator {
1513        int getRegister();
1514        boolean moveNext();
1515        int getCount();
1516        boolean pastEnd();
1517    }
1518
1519    private static class Format35cRegisterIterator implements RegisterIterator {
1520        private final int registerCount;
1521        private final int[] registers;
1522        private int currentRegister = 0;
1523
1524        public Format35cRegisterIterator(FiveRegisterInstruction instruction) {
1525            registerCount = instruction.getRegCount();
1526            registers = new int[]{instruction.getRegisterD(), instruction.getRegisterE(),
1527                                  instruction.getRegisterF(), instruction.getRegisterG(),
1528                                  instruction.getRegisterA()};
1529        }
1530
1531        public int getRegister() {
1532            return registers[currentRegister];
1533        }
1534
1535        public boolean moveNext() {
1536            currentRegister++;
1537            return !pastEnd();
1538        }
1539
1540        public int getCount() {
1541            return registerCount;
1542        }
1543
1544        public boolean pastEnd() {
1545            return currentRegister >= registerCount;
1546        }
1547    }
1548
1549    private static class Format3rcRegisterIterator implements RegisterIterator {
1550        private final int startRegister;
1551        private final int registerCount;
1552        private int currentRegister = 0;
1553
1554        public Format3rcRegisterIterator(RegisterRangeInstruction instruction) {
1555            startRegister = instruction.getStartRegister();
1556            registerCount = instruction.getRegCount();
1557        }
1558
1559        public int getRegister() {
1560            return startRegister + currentRegister;
1561        }
1562
1563        public boolean moveNext() {
1564            currentRegister++;
1565            return !pastEnd();
1566        }
1567
1568        public int getCount() {
1569            return registerCount;
1570        }
1571
1572        public boolean pastEnd() {
1573            return currentRegister >= registerCount;
1574        }
1575    }
1576
1577    private void handleFilledNewArrayCommon(AnalyzedInstruction analyzedInstruction,
1578                                               RegisterIterator registerIterator) {
1579        InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
1580
1581        RegisterType arrayType;
1582        RegisterType arrayImmediateElementType;
1583
1584        Item item = instruction.getReferencedItem();
1585        assert  item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
1586
1587        ClassPath.ClassDef classDef = ClassPath.getClassDef((TypeIdItem)item);
1588
1589        if (classDef.getClassType().charAt(0) != '[') {
1590            throw new ValidationException("Cannot use non-array type \"" + classDef.getClassType() +
1591                "\" with new-array. Use new-instance instead.");
1592        }
1593
1594        ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)classDef;
1595        arrayType = RegisterType.getRegisterType(RegisterType.Category.Reference, classDef);
1596        arrayImmediateElementType = RegisterType.getRegisterTypeForType(
1597                arrayClassDef.getImmediateElementClass().getClassType());
1598        String baseElementType = arrayClassDef.getBaseElementClass().getClassType();
1599        if (baseElementType.charAt(0) == 'J' || baseElementType.charAt(0) == 'D') {
1600            throw new ValidationException("Cannot use filled-new-array to create an array of wide values " +
1601                    "(long or double)");
1602        }
1603
1604        do {
1605            int register = registerIterator.getRegister();
1606            RegisterType elementType = analyzedInstruction.getPreInstructionRegisterType(register);
1607            assert elementType != null;
1608
1609            if (!elementType.canBeAssignedTo(arrayImmediateElementType)) {
1610                throw new ValidationException("Register v" + Integer.toString(register) + " is of type " +
1611                        elementType.toString() + " and is incompatible with the array type " +
1612                        arrayType.type.getClassType());
1613            }
1614        } while (registerIterator.moveNext());
1615    }
1616
1617    private void handleFilledNewArray(AnalyzedInstruction analyzedInstruction) {
1618        FiveRegisterInstruction instruction = (FiveRegisterInstruction)analyzedInstruction.instruction;
1619        handleFilledNewArrayCommon(analyzedInstruction, new Format35cRegisterIterator(instruction));
1620    }
1621
1622    private void handleFilledNewArrayRange(AnalyzedInstruction analyzedInstruction) {
1623        RegisterRangeInstruction instruction = (RegisterRangeInstruction)analyzedInstruction.instruction;
1624
1625        //instruction.getStartRegister() and instruction.getRegCount() both return an int value, but are actually
1626        //unsigned 16 bit values, so we don't have to worry about overflowing an int when adding them together
1627        if (instruction.getStartRegister() + instruction.getRegCount() >= 1<<16) {
1628            throw new ValidationException(String.format("Invalid register range {v%d .. v%d}. The ending register " +
1629                    "is larger than the largest allowed register of v65535.",
1630                    instruction.getStartRegister(),
1631                    instruction.getStartRegister() + instruction.getRegCount() - 1));
1632        }
1633
1634        handleFilledNewArrayCommon(analyzedInstruction, new Format3rcRegisterIterator(instruction));
1635    }
1636
1637    private void handleFillArrayData(AnalyzedInstruction analyzedInstruction) {
1638        SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
1639
1640        int register = instruction.getRegisterA();
1641        RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(register);
1642        assert registerType != null;
1643
1644        if (registerType.category == RegisterType.Category.Null) {
1645            return;
1646        }
1647
1648        if (registerType.category != RegisterType.Category.Reference) {
1649            throw new ValidationException(String.format("Cannot use fill-array-data with non-array register v%d of " +
1650                    "type %s", register, registerType.toString()));
1651        }
1652
1653        assert registerType.type instanceof ClassPath.ArrayClassDef;
1654        ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)registerType.type;
1655
1656        if (arrayClassDef.getArrayDimensions() != 1) {
1657            throw new ValidationException(String.format("Cannot use fill-array-data with array type %s. It can only " +
1658                    "be used with a one-dimensional array of primitives.", arrayClassDef.getClassType()));
1659        }
1660
1661        int elementWidth;
1662        switch (arrayClassDef.getBaseElementClass().getClassType().charAt(0)) {
1663            case 'Z':
1664            case 'B':
1665                elementWidth = 1;
1666                break;
1667            case 'C':
1668            case 'S':
1669                elementWidth = 2;
1670                break;
1671            case 'I':
1672            case 'F':
1673                elementWidth = 4;
1674                break;
1675            case 'J':
1676            case 'D':
1677                elementWidth = 8;
1678                break;
1679            default:
1680                throw new ValidationException(String.format("Cannot use fill-array-data with array type %s. It can " +
1681                        "only be used with a one-dimensional array of primitives.", arrayClassDef.getClassType()));
1682        }
1683
1684
1685        int arrayDataAddressOffset = ((OffsetInstruction)analyzedInstruction.instruction).getTargetAddressOffset();
1686        int arrayDataCodeAddress = getInstructionAddress(analyzedInstruction) + arrayDataAddressOffset;
1687        AnalyzedInstruction arrayDataInstruction = this.instructions.get(arrayDataCodeAddress);
1688        if (arrayDataInstruction == null || arrayDataInstruction.instruction.getFormat() != Format.ArrayData) {
1689            throw new ValidationException(String.format("Could not find an array data structure at code address 0x%x",
1690                    arrayDataCodeAddress));
1691        }
1692
1693        ArrayDataPseudoInstruction arrayDataPseudoInstruction =
1694                (ArrayDataPseudoInstruction)arrayDataInstruction.instruction;
1695
1696        if (elementWidth != arrayDataPseudoInstruction.getElementWidth()) {
1697            throw new ValidationException(String.format("The array data at code address 0x%x does not have the " +
1698                    "correct element width for array type %s. Expecting element width %d, got element width %d.",
1699                    arrayDataCodeAddress, arrayClassDef.getClassType(), elementWidth,
1700                    arrayDataPseudoInstruction.getElementWidth()));
1701        }
1702    }
1703
1704    private void handleThrow(AnalyzedInstruction analyzedInstruction) {
1705        int register = ((SingleRegisterInstruction)analyzedInstruction.instruction).getRegisterA();
1706
1707        RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(register);
1708        assert registerType != null;
1709
1710        if (registerType.category == RegisterType.Category.Null) {
1711            return;
1712        }
1713
1714        if (registerType.category != RegisterType.Category.Reference) {
1715            throw new ValidationException(String.format("Cannot use throw with non-reference type %s in register v%d",
1716                    registerType.toString(), register));
1717        }
1718
1719        assert registerType.type != null;
1720
1721        if (!registerType.type.extendsClass(ClassPath.getClassDef("Ljava/lang/Throwable;"))) {
1722            throw new ValidationException(String.format("Cannot use throw with non-throwable type %s in register v%d",
1723                    registerType.type.getClassType(), register));
1724        }
1725    }
1726
1727    private void handleSwitch(AnalyzedInstruction analyzedInstruction, Format expectedSwitchDataFormat) {
1728        int register = ((SingleRegisterInstruction)analyzedInstruction.instruction).getRegisterA();
1729        int switchCodeAddressOffset = ((OffsetInstruction)analyzedInstruction.instruction).getTargetAddressOffset();
1730
1731        getAndCheckSourceRegister(analyzedInstruction, register, Primitive32BitCategories);
1732
1733        int switchDataCodeAddress = this.getInstructionAddress(analyzedInstruction) + switchCodeAddressOffset;
1734        AnalyzedInstruction switchDataAnalyzedInstruction = instructions.get(switchDataCodeAddress);
1735
1736        if (switchDataAnalyzedInstruction == null ||
1737            switchDataAnalyzedInstruction.instruction.getFormat() != expectedSwitchDataFormat) {
1738            throw new ValidationException(String.format("There is no %s structure at code address 0x%x",
1739                    expectedSwitchDataFormat.name(), switchDataCodeAddress));
1740        }
1741    }
1742
1743    private void handleFloatWideCmp(AnalyzedInstruction analyzedInstruction, EnumSet validCategories) {
1744        ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
1745
1746        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), validCategories);
1747        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), validCategories);
1748
1749        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1750                RegisterType.getRegisterType(RegisterType.Category.Byte, null));
1751    }
1752
1753    private void handleIfEqNe(AnalyzedInstruction analyzedInstruction) {
1754        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
1755
1756        RegisterType registerType1 = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA());
1757        assert registerType1 != null;
1758
1759        RegisterType registerType2 = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1760        assert registerType2 != null;
1761
1762        if (!(
1763                (ReferenceCategories.contains(registerType1.category) &&
1764                ReferenceCategories.contains(registerType2.category))
1765                    ||
1766                (Primitive32BitCategories.contains(registerType1.category) &&
1767                Primitive32BitCategories.contains(registerType2.category))
1768              )) {
1769
1770            throw new ValidationException(String.format("%s cannot be used on registers of dissimilar types %s and " +
1771                    "%s. They must both be a reference type or a primitive 32 bit type.",
1772                    analyzedInstruction.instruction.opcode.name, registerType1.toString(), registerType2.toString()));
1773        }
1774    }
1775
1776    private void handleIf(AnalyzedInstruction analyzedInstruction) {
1777        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
1778
1779        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), Primitive32BitCategories);
1780        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), Primitive32BitCategories);
1781    }
1782
1783    private void handleIfEqzNez(AnalyzedInstruction analyzedInstruction) {
1784        SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
1785
1786        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(),
1787                ReferenceAndPrimitive32BitCategories);
1788    }
1789
1790    private void handleIfz(AnalyzedInstruction analyzedInstruction) {
1791        SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
1792
1793        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), Primitive32BitCategories);
1794    }
1795
1796    private void handle32BitPrimitiveAget(AnalyzedInstruction analyzedInstruction,
1797                                             RegisterType.Category instructionCategory) {
1798        ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
1799
1800        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories);
1801
1802        RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1803        assert arrayRegisterType != null;
1804
1805        if (arrayRegisterType.category != RegisterType.Category.Null) {
1806            if (arrayRegisterType.category != RegisterType.Category.Reference) {
1807                throw new ValidationException(String.format("Cannot use %s with non-array type %s",
1808                        analyzedInstruction.instruction.opcode.name, arrayRegisterType.category.toString()));
1809            }
1810
1811            assert arrayRegisterType.type != null;
1812            if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
1813                throw new ValidationException(String.format("Cannot use %s with non-array type %s",
1814                        analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType()));
1815            }
1816
1817            assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
1818            ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type;
1819
1820            if (arrayClassDef.getArrayDimensions() != 1) {
1821                throw new ValidationException(String.format("Cannot use %s with multi-dimensional array type %s",
1822                        analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType()));
1823            }
1824
1825            RegisterType arrayBaseType =
1826                    RegisterType.getRegisterTypeForType(arrayClassDef.getBaseElementClass().getClassType());
1827            if (!checkArrayFieldAssignment(arrayBaseType.category, instructionCategory)) {
1828                throw new ValidationException(String.format("Cannot use %s with array type %s. Incorrect array type " +
1829                        "for the instruction.", analyzedInstruction.instruction.opcode.name,
1830                        arrayRegisterType.type.getClassType()));
1831            }
1832        }
1833
1834        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1835                RegisterType.getRegisterType(instructionCategory, null));
1836    }
1837
1838    private void handleAgetWide(AnalyzedInstruction analyzedInstruction) {
1839        ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
1840
1841        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories);
1842
1843        RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1844        assert arrayRegisterType != null;
1845
1846        if (arrayRegisterType.category != RegisterType.Category.Null) {
1847            if (arrayRegisterType.category != RegisterType.Category.Reference) {
1848                throw new ValidationException(String.format("Cannot use aget-wide with non-array type %s",
1849                        arrayRegisterType.category.toString()));
1850            }
1851
1852            assert arrayRegisterType.type != null;
1853            if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
1854                throw new ValidationException(String.format("Cannot use aget-wide with non-array type %s",
1855                        arrayRegisterType.type.getClassType()));
1856            }
1857
1858            assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
1859            ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type;
1860
1861            if (arrayClassDef.getArrayDimensions() != 1) {
1862                throw new ValidationException(String.format("Cannot use aget-wide with multi-dimensional array type %s",
1863                        arrayRegisterType.type.getClassType()));
1864            }
1865
1866            char arrayBaseType = arrayClassDef.getBaseElementClass().getClassType().charAt(0);
1867            if (arrayBaseType == 'J') {
1868                setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1869                        RegisterType.getRegisterType(RegisterType.Category.LongLo, null));
1870            } else if (arrayBaseType == 'D') {
1871                setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1872                        RegisterType.getRegisterType(RegisterType.Category.DoubleLo, null));
1873            } else {
1874                throw new ValidationException(String.format("Cannot use aget-wide with array type %s. Incorrect " +
1875                        "array type for the instruction.", arrayRegisterType.type.getClassType()));
1876            }
1877        } else {
1878            setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1879                        RegisterType.getRegisterType(RegisterType.Category.LongLo, null));
1880        }
1881    }
1882
1883    private void handleAgetObject(AnalyzedInstruction analyzedInstruction) {
1884        ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
1885
1886        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories);
1887
1888        RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1889        assert arrayRegisterType != null;
1890
1891        if (arrayRegisterType.category != RegisterType.Category.Null) {
1892            if (arrayRegisterType.category != RegisterType.Category.Reference) {
1893                throw new ValidationException(String.format("Cannot use aget-object with non-array type %s",
1894                        arrayRegisterType.category.toString()));
1895            }
1896
1897            assert arrayRegisterType.type != null;
1898            if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
1899                throw new ValidationException(String.format("Cannot use aget-object with non-array type %s",
1900                        arrayRegisterType.type.getClassType()));
1901            }
1902
1903            assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
1904            ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type;
1905
1906            ClassPath.ClassDef elementClassDef = arrayClassDef.getImmediateElementClass();
1907            char elementTypePrefix = elementClassDef.getClassType().charAt(0);
1908            if (elementTypePrefix != 'L' && elementTypePrefix != '[') {
1909                throw new ValidationException(String.format("Cannot use aget-object with array type %s. Incorrect " +
1910                        "array type for the instruction.", arrayRegisterType.type.getClassType()));
1911            }
1912
1913            setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1914                    RegisterType.getRegisterType(RegisterType.Category.Reference, elementClassDef));
1915        } else {
1916            setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1917                    RegisterType.getRegisterType(RegisterType.Category.Null, null));
1918        }
1919    }
1920
1921    private void handle32BitPrimitiveAput(AnalyzedInstruction analyzedInstruction,
1922                                             RegisterType.Category instructionCategory) {
1923        ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
1924
1925        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories);
1926
1927        RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA());
1928        assert sourceRegisterType != null;
1929        RegisterType instructionRegisterType = RegisterType.getRegisterType(instructionCategory, null);
1930        if (!sourceRegisterType.canBeAssignedTo(instructionRegisterType)) {
1931            throw new ValidationException(String.format("Cannot use %s with source register type %s.",
1932                    analyzedInstruction.instruction.opcode.name, sourceRegisterType.toString()));
1933        }
1934
1935
1936        RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1937        assert arrayRegisterType != null;
1938
1939        if (arrayRegisterType.category != RegisterType.Category.Null) {
1940            if (arrayRegisterType.category != RegisterType.Category.Reference) {
1941                throw new ValidationException(String.format("Cannot use %s with non-array type %s",
1942                        analyzedInstruction.instruction.opcode.name, arrayRegisterType.category.toString()));
1943            }
1944
1945            assert arrayRegisterType.type != null;
1946            if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
1947                throw new ValidationException(String.format("Cannot use %s with non-array type %s",
1948                        analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType()));
1949            }
1950
1951            assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
1952            ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type;
1953
1954            if (arrayClassDef.getArrayDimensions() != 1) {
1955                throw new ValidationException(String.format("Cannot use %s with multi-dimensional array type %s",
1956                        analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType()));
1957            }
1958
1959            RegisterType arrayBaseType =
1960                    RegisterType.getRegisterTypeForType(arrayClassDef.getBaseElementClass().getClassType());
1961            if (!checkArrayFieldAssignment(arrayBaseType.category, instructionCategory)) {
1962                throw new ValidationException(String.format("Cannot use %s with array type %s. Incorrect array type " +
1963                        "for the instruction.", analyzedInstruction.instruction.opcode.name,
1964                        arrayRegisterType.type.getClassType()));
1965            }
1966        }
1967    }
1968
1969    private void handleAputWide(AnalyzedInstruction analyzedInstruction) {
1970        ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
1971
1972        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories);
1973        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), WideLowCategories);
1974
1975        RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1976        assert arrayRegisterType != null;
1977
1978        if (arrayRegisterType.category != RegisterType.Category.Null) {
1979            if (arrayRegisterType.category != RegisterType.Category.Reference) {
1980                throw new ValidationException(String.format("Cannot use aput-wide with non-array type %s",
1981                        arrayRegisterType.category.toString()));
1982            }
1983
1984            assert arrayRegisterType.type != null;
1985            if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
1986                throw new ValidationException(String.format("Cannot use aput-wide with non-array type %s",
1987                        arrayRegisterType.type.getClassType()));
1988            }
1989
1990            assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
1991            ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type;
1992
1993            if (arrayClassDef.getArrayDimensions() != 1) {
1994                throw new ValidationException(String.format("Cannot use aput-wide with multi-dimensional array type %s",
1995                        arrayRegisterType.type.getClassType()));
1996            }
1997
1998            char arrayBaseType = arrayClassDef.getBaseElementClass().getClassType().charAt(0);
1999            if (arrayBaseType != 'J' && arrayBaseType != 'D') {
2000                throw new ValidationException(String.format("Cannot use aput-wide with array type %s. Incorrect " +
2001                        "array type for the instruction.", arrayRegisterType.type.getClassType()));
2002            }
2003        }
2004    }
2005
2006    private void handleAputObject(AnalyzedInstruction analyzedInstruction) {
2007        ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
2008
2009        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(), Primitive32BitCategories);
2010
2011        RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA());
2012        assert sourceRegisterType != null;
2013
2014        //TODO: ensure sourceRegisterType is a Reference type?
2015
2016        RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
2017        assert arrayRegisterType != null;
2018
2019        if (arrayRegisterType.category != RegisterType.Category.Null) {
2020            //don't check the source type against the array type, just make sure it is an array of reference types
2021
2022            if (arrayRegisterType.category != RegisterType.Category.Reference) {
2023                throw new ValidationException(String.format("Cannot use aget-object with non-array type %s",
2024                        arrayRegisterType.category.toString()));
2025            }
2026
2027            assert arrayRegisterType.type != null;
2028            if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
2029                throw new ValidationException(String.format("Cannot use aget-object with non-array type %s",
2030                        arrayRegisterType.type.getClassType()));
2031            }
2032
2033            assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
2034            ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type;
2035
2036            ClassPath.ClassDef elementClassDef = arrayClassDef.getImmediateElementClass();
2037            char elementTypePrefix = elementClassDef.getClassType().charAt(0);
2038            if (elementTypePrefix != 'L' && elementTypePrefix != '[') {
2039                throw new ValidationException(String.format("Cannot use aget-object with array type %s. Incorrect " +
2040                        "array type for the instruction.", arrayRegisterType.type.getClassType()));
2041            }
2042        }
2043    }
2044
2045    private void handle32BitPrimitiveIget(AnalyzedInstruction analyzedInstruction,
2046                                             RegisterType.Category instructionCategory) {
2047        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
2048
2049        RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
2050                ReferenceOrUninitThisCategories);
2051
2052        //TODO: check access
2053        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
2054        assert referencedItem instanceof FieldIdItem;
2055        FieldIdItem field = (FieldIdItem)referencedItem;
2056
2057        if (objectRegisterType.category != RegisterType.Category.Null &&
2058            !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) {
2059            throw new ValidationException(String.format("Cannot access field %s through type %s",
2060                    field.getFieldString(), objectRegisterType.type.getClassType()));
2061        }
2062
2063        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
2064
2065        if (!checkArrayFieldAssignment(fieldType.category, instructionCategory)) {
2066                throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
2067                        "for the instruction.", analyzedInstruction.instruction.opcode.name,
2068                        field.getFieldString()));
2069        }
2070
2071        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
2072                RegisterType.getRegisterType(instructionCategory, null));
2073    }
2074
2075    private void handleIgetWide(AnalyzedInstruction analyzedInstruction) {
2076        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
2077
2078        RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
2079                ReferenceOrUninitThisCategories);
2080
2081        //TODO: check access
2082        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
2083        assert referencedItem instanceof FieldIdItem;
2084        FieldIdItem field = (FieldIdItem)referencedItem;
2085
2086        if (objectRegisterType.category != RegisterType.Category.Null &&
2087            !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) {
2088            throw new ValidationException(String.format("Cannot access field %s through type %s",
2089                    field.getFieldString(), objectRegisterType.type.getClassType()));
2090        }
2091
2092        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
2093
2094        if (!WideLowCategories.contains(fieldType.category)) {
2095            throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
2096                    "for the instruction.", analyzedInstruction.instruction.opcode.name,
2097                    field.getFieldString()));
2098        }
2099
2100        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, fieldType);
2101    }
2102
2103    private void handleIgetObject(AnalyzedInstruction analyzedInstruction) {
2104        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
2105
2106        RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
2107                ReferenceOrUninitThisCategories);
2108
2109        //TODO: check access
2110        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
2111        assert referencedItem instanceof FieldIdItem;
2112        FieldIdItem field = (FieldIdItem)referencedItem;
2113
2114        if (objectRegisterType.category != RegisterType.Category.Null &&
2115            !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) {
2116            throw new ValidationException(String.format("Cannot access field %s through type %s",
2117                    field.getFieldString(), objectRegisterType.type.getClassType()));
2118        }
2119
2120        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
2121
2122        if (fieldType.category != RegisterType.Category.Reference) {
2123            throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
2124                        "for the instruction.", analyzedInstruction.instruction.opcode.name,
2125                        field.getFieldString()));
2126        }
2127
2128        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, fieldType);
2129    }
2130
2131    private void handle32BitPrimitiveIput(AnalyzedInstruction analyzedInstruction,
2132                                             RegisterType.Category instructionCategory) {
2133        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
2134
2135        RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
2136                ReferenceOrUninitThisCategories);
2137
2138        RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA());
2139        assert sourceRegisterType != null;
2140
2141        //per CodeVerify.c in dalvik:
2142        //java generates synthetic functions that write byte values into boolean fields
2143        if (sourceRegisterType.category == RegisterType.Category.Byte &&
2144            instructionCategory == RegisterType.Category.Boolean) {
2145
2146            sourceRegisterType = RegisterType.getRegisterType(RegisterType.Category.Boolean, null);
2147        }
2148
2149        RegisterType instructionRegisterType = RegisterType.getRegisterType(instructionCategory, null);
2150        if (!sourceRegisterType.canBeAssignedTo(instructionRegisterType)) {
2151            throw new ValidationException(String.format("Cannot use %s with source register type %s.",
2152                    analyzedInstruction.instruction.opcode.name, sourceRegisterType.toString()));
2153        }
2154
2155
2156        //TODO: check access
2157        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
2158        assert referencedItem instanceof FieldIdItem;
2159        FieldIdItem field = (FieldIdItem)referencedItem;
2160
2161        if (objectRegisterType.category != RegisterType.Category.Null &&
2162            !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) {
2163            throw new ValidationException(String.format("Cannot access field %s through type %s",
2164                    field.getFieldString(), objectRegisterType.type.getClassType()));
2165        }
2166
2167        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
2168
2169        if (!checkArrayFieldAssignment(fieldType.category, instructionCategory)) {
2170                throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
2171                        "for the instruction.", analyzedInstruction.instruction.opcode.name,
2172                        field.getFieldString()));
2173        }
2174    }
2175
2176    private void handleIputWide(AnalyzedInstruction analyzedInstruction) {
2177        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
2178
2179        RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
2180                ReferenceOrUninitThisCategories);
2181
2182        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), WideLowCategories);
2183
2184        //TODO: check access
2185        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
2186        assert referencedItem instanceof FieldIdItem;
2187        FieldIdItem field = (FieldIdItem)referencedItem;
2188
2189        if (objectRegisterType.category != RegisterType.Category.Null &&
2190                !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) {
2191            throw new ValidationException(String.format("Cannot access field %s through type %s",
2192                    field.getFieldString(), objectRegisterType.type.getClassType()));
2193        }
2194
2195        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
2196
2197        if (!WideLowCategories.contains(fieldType.category)) {
2198            throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
2199                    "for the instruction.", analyzedInstruction.instruction.opcode.name,
2200                    field.getFieldString()));
2201        }
2202    }
2203
2204    private void handleIputObject(AnalyzedInstruction analyzedInstruction) {
2205        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
2206
2207        RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
2208                ReferenceOrUninitThisCategories);
2209
2210        RegisterType sourceRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(),
2211                ReferenceCategories);
2212
2213        //TODO: check access
2214        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
2215        assert referencedItem instanceof FieldIdItem;
2216        FieldIdItem field = (FieldIdItem)referencedItem;
2217
2218        if (objectRegisterType.category != RegisterType.Category.Null &&
2219            !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) {
2220            throw new ValidationException(String.format("Cannot access field %s through type %s",
2221                    field.getFieldString(), objectRegisterType.type.getClassType()));
2222        }
2223
2224        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
2225
2226        if (fieldType.category != RegisterType.Category.Reference) {
2227            throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
2228                        "for the instruction.", analyzedInstruction.instruction.opcode.name,
2229                        field.getFieldString()));
2230        }
2231
2232        if (sourceRegisterType.category != RegisterType.Category.Null &&
2233            !fieldType.type.isInterface() &&
2234            !sourceRegisterType.type.extendsClass(fieldType.type)) {
2235
2236            throw new ValidationException(String.format("Cannot store a value of type %s into a field of type %s",
2237                    sourceRegisterType.type.getClassType(), fieldType.type.getClassType()));
2238        }
2239    }
2240
2241    private void handle32BitPrimitiveSget(AnalyzedInstruction analyzedInstruction,
2242                                             RegisterType.Category instructionCategory) {
2243        //TODO: check access
2244        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
2245        assert referencedItem instanceof FieldIdItem;
2246        FieldIdItem field = (FieldIdItem)referencedItem;
2247
2248        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
2249
2250        if (!checkArrayFieldAssignment(fieldType.category, instructionCategory)) {
2251                throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
2252                        "for the instruction.", analyzedInstruction.instruction.opcode.name,
2253                        field.getFieldString()));
2254        }
2255
2256        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
2257                RegisterType.getRegisterType(instructionCategory, null));
2258    }
2259
2260    private void handleSgetWide(AnalyzedInstruction analyzedInstruction) {
2261        //TODO: check access
2262        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
2263        assert referencedItem instanceof FieldIdItem;
2264        FieldIdItem field = (FieldIdItem)referencedItem;
2265
2266        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
2267
2268
2269        if (fieldType.category != RegisterType.Category.LongLo &&
2270            fieldType.category != RegisterType.Category.DoubleLo) {
2271
2272            throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
2273                    "for the instruction.", analyzedInstruction.instruction.opcode.name,
2274                    field.getFieldString()));
2275        }
2276
2277        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, fieldType);
2278    }
2279
2280    private void handleSgetObject(AnalyzedInstruction analyzedInstruction) {
2281        //TODO: check access
2282        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
2283        assert referencedItem instanceof FieldIdItem;
2284        FieldIdItem field = (FieldIdItem)referencedItem;
2285
2286        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
2287
2288        if (fieldType.category != RegisterType.Category.Reference) {
2289                throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
2290                        "for the instruction.", analyzedInstruction.instruction.opcode.name,
2291                        field.getFieldString()));
2292        }
2293
2294        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, fieldType);
2295    }
2296
2297    private void handle32BitPrimitiveSput(AnalyzedInstruction analyzedInstruction,
2298                                             RegisterType.Category instructionCategory) {
2299        SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
2300
2301        RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA());
2302        assert sourceRegisterType != null;
2303
2304        //per CodeVerify.c in dalvik:
2305        //java generates synthetic functions that write byte values into boolean fields
2306        if (sourceRegisterType.category == RegisterType.Category.Byte &&
2307            instructionCategory == RegisterType.Category.Boolean) {
2308
2309            sourceRegisterType = RegisterType.getRegisterType(RegisterType.Category.Boolean, null);
2310        }
2311
2312        RegisterType instructionRegisterType = RegisterType.getRegisterType(instructionCategory, null);
2313        if (!sourceRegisterType.canBeAssignedTo(instructionRegisterType)) {
2314            throw new ValidationException(String.format("Cannot use %s with source register type %s.",
2315                    analyzedInstruction.instruction.opcode.name, sourceRegisterType.toString()));
2316        }
2317
2318        //TODO: check access
2319        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
2320        assert referencedItem instanceof FieldIdItem;
2321        FieldIdItem field = (FieldIdItem)referencedItem;
2322
2323        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
2324
2325        if (!checkArrayFieldAssignment(fieldType.category, instructionCategory)) {
2326                throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
2327                        "for the instruction.", analyzedInstruction.instruction.opcode.name,
2328                        field.getFieldString()));
2329        }
2330    }
2331
2332    private void handleSputWide(AnalyzedInstruction analyzedInstruction) {
2333        SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
2334
2335
2336        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(), WideLowCategories);
2337
2338        //TODO: check access
2339        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
2340        assert referencedItem instanceof FieldIdItem;
2341        FieldIdItem field = (FieldIdItem)referencedItem;
2342
2343        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
2344
2345        if (!WideLowCategories.contains(fieldType.category)) {
2346                throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
2347                        "for the instruction.", analyzedInstruction.instruction.opcode.name,
2348                        field.getFieldString()));
2349        }
2350    }
2351
2352    private void handleSputObject(AnalyzedInstruction analyzedInstruction) {
2353        SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
2354
2355        RegisterType sourceRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(),
2356                ReferenceCategories);
2357
2358        //TODO: check access
2359        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
2360        assert referencedItem instanceof FieldIdItem;
2361        FieldIdItem field = (FieldIdItem)referencedItem;
2362
2363        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
2364
2365        if (fieldType.category != RegisterType.Category.Reference) {
2366            throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
2367                        "for the instruction.", analyzedInstruction.instruction.opcode.name,
2368                        field.getFieldString()));
2369        }
2370
2371        if (sourceRegisterType.category != RegisterType.Category.Null &&
2372            !fieldType.type.isInterface() &&
2373            !sourceRegisterType.type.extendsClass(fieldType.type)) {
2374
2375            throw new ValidationException(String.format("Cannot store a value of type %s into a field of type %s",
2376                    sourceRegisterType.type.getClassType(), fieldType.type.getClassType()));
2377        }
2378    }
2379
2380    private void handleInvoke(AnalyzedInstruction analyzedInstruction, int invokeType) {
2381        FiveRegisterInstruction instruction = (FiveRegisterInstruction)analyzedInstruction.instruction;
2382        handleInvokeCommon(analyzedInstruction, false, invokeType, new Format35cRegisterIterator(instruction));
2383    }
2384
2385    private void handleInvokeRange(AnalyzedInstruction analyzedInstruction, int invokeType) {
2386        RegisterRangeInstruction instruction = (RegisterRangeInstruction)analyzedInstruction.instruction;
2387        handleInvokeCommon(analyzedInstruction, true, invokeType, new Format3rcRegisterIterator(instruction));
2388    }
2389
2390    private static final int INVOKE_VIRTUAL = 0x01;
2391    private static final int INVOKE_SUPER = 0x02;
2392    private static final int INVOKE_DIRECT = 0x04;
2393    private static final int INVOKE_INTERFACE = 0x08;
2394    private static final int INVOKE_STATIC = 0x10;
2395
2396    private void handleInvokeCommon(AnalyzedInstruction analyzedInstruction, boolean isRange, int invokeType,
2397                                       RegisterIterator registers) {
2398        InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
2399
2400        //TODO: check access
2401
2402        Item item = instruction.getReferencedItem();
2403        assert item.getItemType() == ItemType.TYPE_METHOD_ID_ITEM;
2404        MethodIdItem methodIdItem = (MethodIdItem)item;
2405
2406        TypeIdItem methodClass = methodIdItem.getContainingClass();
2407        boolean isInit = false;
2408
2409        if (methodIdItem.getMethodName().getStringValue().charAt(0) == '<') {
2410            if ((invokeType & INVOKE_DIRECT) != 0) {
2411                isInit = true;
2412            } else {
2413                throw new ValidationException(String.format("Cannot call constructor %s with %s",
2414                        methodIdItem.getMethodString(), analyzedInstruction.instruction.opcode.name));
2415            }
2416        }
2417
2418        ClassPath.ClassDef methodClassDef = ClassPath.getClassDef(methodClass);
2419        if ((invokeType & INVOKE_INTERFACE) != 0) {
2420            if (!methodClassDef.isInterface()) {
2421                throw new ValidationException(String.format("Cannot call method %s with %s. %s is not an interface " +
2422                        "class.", methodIdItem.getMethodString(), analyzedInstruction.instruction.opcode.name,
2423                        methodClassDef.getClassType()));
2424            }
2425        } else {
2426            if (methodClassDef.isInterface()) {
2427                throw new ValidationException(String.format("Cannot call method %s with %s. %s is an interface class." +
2428                        " Use invoke-interface or invoke-interface/range instead.", methodIdItem.getMethodString(),
2429                        analyzedInstruction.instruction.opcode.name, methodClassDef.getClassType()));
2430            }
2431        }
2432
2433        if ((invokeType & INVOKE_SUPER) != 0) {
2434            ClassPath.ClassDef currentMethodClassDef = ClassPath.getClassDef(encodedMethod.method.getContainingClass());
2435            if (currentMethodClassDef.getSuperclass() == null) {
2436                throw new ValidationException(String.format("Cannot call method %s with %s. %s has no superclass",
2437                        methodIdItem.getMethodString(), analyzedInstruction.instruction.opcode.name,
2438                        methodClassDef.getSuperclass().getClassType()));
2439            }
2440
2441            if (!currentMethodClassDef.getSuperclass().extendsClass(methodClassDef)) {
2442                throw new ValidationException(String.format("Cannot call method %s with %s. %s is not an ancestor " +
2443                        "of the current class %s", methodIdItem.getMethodString(),
2444                        analyzedInstruction.instruction.opcode.name, methodClass.getTypeDescriptor(),
2445                        encodedMethod.method.getContainingClass().getTypeDescriptor()));
2446            }
2447
2448            if (!currentMethodClassDef.getSuperclass().hasVirtualMethod(methodIdItem.getVirtualMethodString())) {
2449                throw new ValidationException(String.format("Cannot call method %s with %s. The superclass %s has" +
2450                        "no such method", methodIdItem.getMethodString(),
2451                        analyzedInstruction.instruction.opcode.name, methodClassDef.getSuperclass().getClassType()));
2452            }
2453        }
2454
2455        assert isRange || registers.getCount() <= 5;
2456
2457        TypeListItem typeListItem = methodIdItem.getPrototype().getParameters();
2458        int methodParameterRegisterCount;
2459        if (typeListItem == null) {
2460            methodParameterRegisterCount = 0;
2461        } else {
2462            methodParameterRegisterCount = typeListItem.getRegisterCount();
2463        }
2464
2465        if ((invokeType & INVOKE_STATIC) == 0) {
2466            methodParameterRegisterCount++;
2467        }
2468
2469        if (methodParameterRegisterCount != registers.getCount()) {
2470            throw new ValidationException(String.format("The number of registers does not match the number of " +
2471                    "parameters for method %s. Expecting %d registers, got %d.", methodIdItem.getMethodString(),
2472                    methodParameterRegisterCount + 1, registers.getCount()));
2473        }
2474
2475        RegisterType objectRegisterType = null;
2476        int objectRegister = 0;
2477        if ((invokeType & INVOKE_STATIC) == 0) {
2478            objectRegister = registers.getRegister();
2479            registers.moveNext();
2480
2481            objectRegisterType = analyzedInstruction.getPreInstructionRegisterType(objectRegister);
2482            assert objectRegisterType != null;
2483            if (objectRegisterType.category == RegisterType.Category.UninitRef ||
2484                    objectRegisterType.category == RegisterType.Category.UninitThis) {
2485
2486                if (!isInit) {
2487                    throw new ValidationException(String.format("Cannot invoke non-<init> method %s on uninitialized " +
2488                            "reference type %s", methodIdItem.getMethodString(),
2489                            objectRegisterType.type.getClassType()));
2490                }
2491            } else if (objectRegisterType.category == RegisterType.Category.Reference) {
2492                if (isInit) {
2493                    throw new ValidationException(String.format("Cannot invoke %s on initialized reference type %s",
2494                            methodIdItem.getMethodString(), objectRegisterType.type.getClassType()));
2495                }
2496            } else if (objectRegisterType.category == RegisterType.Category.Null) {
2497                if (isInit) {
2498                    throw new ValidationException(String.format("Cannot invoke %s on a null reference",
2499                            methodIdItem.getMethodString()));
2500                }
2501            }
2502            else {
2503                throw new ValidationException(String.format("Cannot invoke %s on non-reference type %s",
2504                        methodIdItem.getMethodString(), objectRegisterType.toString()));
2505            }
2506
2507            if (isInit) {
2508                if (objectRegisterType.type.getSuperclass() == methodClassDef) {
2509                    if (!encodedMethod.method.getMethodName().getStringValue().equals("<init>")) {
2510                        throw new ValidationException(String.format("Cannot call %s on type %s. The object type must " +
2511                                "match the method type exactly", methodIdItem.getMethodString(),
2512                                objectRegisterType.type.getClassType()));
2513                    }
2514                }
2515            }
2516
2517            if ((invokeType & INVOKE_INTERFACE) == 0 && objectRegisterType.category != RegisterType.Category.Null &&
2518                    !objectRegisterType.type.extendsClass(methodClassDef)) {
2519
2520               throw new ValidationException(String.format("Cannot call method %s on an object of type %s, which " +
2521                       "does not extend %s.", methodIdItem.getMethodString(), objectRegisterType.type.getClassType(),
2522                        methodClassDef.getClassType()));
2523            }
2524        }
2525
2526        if (typeListItem != null) {
2527            List<TypeIdItem> parameterTypes = typeListItem.getTypes();
2528            int parameterTypeIndex = 0;
2529            while (!registers.pastEnd()) {
2530                assert parameterTypeIndex < parameterTypes.size();
2531                RegisterType parameterType =
2532                        RegisterType.getRegisterTypeForTypeIdItem(parameterTypes.get(parameterTypeIndex));
2533
2534                int register = registers.getRegister();
2535
2536                RegisterType parameterRegisterType;
2537                if (WideLowCategories.contains(parameterType.category)) {
2538                    parameterRegisterType = getAndCheckSourceRegister(analyzedInstruction, register, WideLowCategories);
2539
2540                    if (!registers.moveNext()) {
2541                        throw new ValidationException(String.format("No 2nd register specified for wide register pair v%d",
2542                                parameterTypeIndex+1));
2543                    }
2544                    int nextRegister = registers.getRegister();
2545
2546                    if (nextRegister != register + 1) {
2547                        throw new ValidationException(String.format("Invalid wide register pair (v%d, v%d). Registers " +
2548                                "must be consecutive.", register, nextRegister));
2549                    }
2550                } else {
2551                    parameterRegisterType = analyzedInstruction.getPreInstructionRegisterType(register);
2552                }
2553
2554                assert parameterRegisterType != null;
2555
2556                if (!parameterRegisterType.canBeAssignedTo(parameterType)) {
2557                    throw new ValidationException(
2558                            String.format("Invalid register type %s for parameter %d %s.",
2559                                    parameterRegisterType.toString(), parameterTypeIndex+1,
2560                                    parameterType.toString()));
2561                }
2562
2563                parameterTypeIndex++;
2564                registers.moveNext();
2565            }
2566        }
2567
2568
2569        //TODO: need to ensure the "this" register is initialized, in a constructor method
2570        if (isInit) {
2571            setPostRegisterTypeAndPropagateChanges(analyzedInstruction, objectRegister,
2572                    RegisterType.getRegisterType(RegisterType.Category.Reference, objectRegisterType.type));
2573
2574            for (int i=0; i<analyzedInstruction.postRegisterMap.length; i++) {
2575                RegisterType postInstructionRegisterType = analyzedInstruction.postRegisterMap[i];
2576                if (postInstructionRegisterType.category == RegisterType.Category.Unknown) {
2577                    RegisterType preInstructionRegisterType =
2578                            analyzedInstruction.getPreInstructionRegisterType(i);
2579
2580                    if (preInstructionRegisterType.category == RegisterType.Category.UninitRef ||
2581                        preInstructionRegisterType.category == RegisterType.Category.UninitThis) {
2582
2583                        RegisterType registerType;
2584                        if (preInstructionRegisterType == objectRegisterType) {
2585                            registerType = analyzedInstruction.postRegisterMap[objectRegister];
2586                        } else {
2587                            registerType = preInstructionRegisterType;
2588                        }
2589
2590                        setPostRegisterTypeAndPropagateChanges(analyzedInstruction, i, registerType);
2591                    }
2592                }
2593            }
2594        }
2595    }
2596
2597    private void handleUnaryOp(AnalyzedInstruction analyzedInstruction, EnumSet validSourceCategories,
2598                            RegisterType.Category destRegisterCategory) {
2599        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
2600
2601        getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(), validSourceCategories);
2602
2603        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
2604                RegisterType.getRegisterType(destRegisterCategory, null));
2605    }
2606
2607    private void handleBinaryOp(AnalyzedInstruction analyzedInstruction, EnumSet validSource1Categories,
2608                                EnumSet validSource2Categories, RegisterType.Category destRegisterCategory,
2609                                boolean checkForBoolean) {
2610        ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
2611
2612        RegisterType source1RegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
2613                validSource1Categories);
2614        RegisterType source2RegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterC(),
2615                validSource2Categories);
2616
2617        if (checkForBoolean) {
2618            if (BooleanCategories.contains(source1RegisterType.category) &&
2619                BooleanCategories.contains(source2RegisterType.category)) {
2620
2621                destRegisterCategory = RegisterType.Category.Boolean;
2622            }
2623        }
2624
2625        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
2626                RegisterType.getRegisterType(destRegisterCategory, null));
2627    }
2628
2629    private void handleBinary2AddrOp(AnalyzedInstruction analyzedInstruction, EnumSet validSource1Categories,
2630                                EnumSet validSource2Categories, RegisterType.Category destRegisterCategory,
2631                                boolean checkForBoolean) {
2632        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
2633
2634        RegisterType source1RegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterA(),
2635                validSource1Categories);
2636        RegisterType source2RegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
2637                validSource2Categories);
2638
2639        if (checkForBoolean) {
2640            if (BooleanCategories.contains(source1RegisterType.category) &&
2641                BooleanCategories.contains(source2RegisterType.category)) {
2642
2643                destRegisterCategory = RegisterType.Category.Boolean;
2644            }
2645        }
2646
2647        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
2648                RegisterType.getRegisterType(destRegisterCategory, null));
2649    }
2650
2651    private void handleLiteralBinaryOp(AnalyzedInstruction analyzedInstruction, EnumSet validSourceCategories,
2652                                RegisterType.Category destRegisterCategory, boolean checkForBoolean) {
2653        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
2654
2655        RegisterType sourceRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
2656                validSourceCategories);
2657
2658        if (checkForBoolean) {
2659            if (BooleanCategories.contains(sourceRegisterType.category)) {
2660                long literal = ((LiteralInstruction)analyzedInstruction.instruction).getLiteral();
2661                if (literal == 0 || literal == 1) {
2662                    destRegisterCategory = RegisterType.Category.Boolean;
2663                }
2664            }
2665        }
2666
2667        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
2668                RegisterType.getRegisterType(destRegisterCategory, null));
2669    }
2670
2671    private RegisterType.Category getDestTypeForLiteralShiftRight(AnalyzedInstruction analyzedInstruction,
2672                                                                  boolean signedShift) {
2673        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
2674
2675        RegisterType sourceRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
2676                Primitive32BitCategories);
2677        long literalShift = ((LiteralInstruction)analyzedInstruction.instruction).getLiteral();
2678
2679        if (literalShift == 0) {
2680            return sourceRegisterType.category;
2681        }
2682
2683        RegisterType.Category destRegisterCategory;
2684        if (!signedShift) {
2685            destRegisterCategory = RegisterType.Category.Integer;
2686        } else {
2687            destRegisterCategory = sourceRegisterType.category;
2688        }
2689
2690        if (literalShift >= 32) {
2691            //TODO: add warning
2692            return destRegisterCategory;
2693        }
2694
2695        switch (sourceRegisterType.category) {
2696            case Integer:
2697            case Float:
2698                if (!signedShift) {
2699                    if (literalShift > 24) {
2700                        return RegisterType.Category.PosByte;
2701                    }
2702                    if (literalShift >= 16) {
2703                        return RegisterType.Category.Char;
2704                    }
2705                } else {
2706                    if (literalShift >= 24) {
2707                        return RegisterType.Category.Byte;
2708                    }
2709                    if (literalShift >= 16) {
2710                        return RegisterType.Category.Short;
2711                    }
2712                }
2713                break;
2714            case Short:
2715                if (signedShift && literalShift >= 8) {
2716                    return RegisterType.Category.Byte;
2717                }
2718                break;
2719            case PosShort:
2720                if (literalShift >= 8) {
2721                    return RegisterType.Category.PosByte;
2722                }
2723                break;
2724            case Char:
2725                if (literalShift > 8) {
2726                    return RegisterType.Category.PosByte;
2727                }
2728                break;
2729            case Byte:
2730                break;
2731            case PosByte:
2732                return RegisterType.Category.PosByte;
2733            case Null:
2734            case One:
2735            case Boolean:
2736                return RegisterType.Category.Null;
2737            default:
2738                assert false;
2739        }
2740
2741        return destRegisterCategory;
2742    }
2743
2744
2745    private void handleExecuteInline(AnalyzedInstruction analyzedInstruction) {
2746        if (deodexUtil == null) {
2747            throw new ValidationException("Cannot analyze an odexed instruction unless we are deodexing");
2748        }
2749
2750        Instruction35ms instruction = (Instruction35ms)analyzedInstruction.instruction;
2751
2752        int methodIndex = instruction.getMethodIndex();
2753        DeodexUtil.InlineMethod inlineMethod = deodexUtil.lookupInlineMethod(methodIndex);
2754        MethodIdItem inlineMethodIdItem = inlineMethod.getMethodIdItem();
2755        if (inlineMethodIdItem == null) {
2756            throw new ValidationException(String.format("Cannot load inline method with index %d", methodIndex));
2757        }
2758
2759        Opcode deodexedOpcode = null;
2760        switch (inlineMethod.methodType) {
2761            case DeodexUtil.Direct:
2762                deodexedOpcode = Opcode.INVOKE_DIRECT;
2763                break;
2764            case DeodexUtil.Static:
2765                deodexedOpcode = Opcode.INVOKE_STATIC;
2766                break;
2767            case DeodexUtil.Virtual:
2768                deodexedOpcode = Opcode.INVOKE_VIRTUAL;
2769                break;
2770            default:
2771                assert false;
2772        }
2773
2774        Instruction35c deodexedInstruction = new Instruction35c(deodexedOpcode, instruction.getRegCount(),
2775                instruction.getRegisterD(), instruction.getRegisterE(), instruction.getRegisterF(),
2776                instruction.getRegisterG(), instruction.getRegisterA(), inlineMethodIdItem);
2777
2778        analyzedInstruction.setDeodexedInstruction(deodexedInstruction);
2779
2780        analyzeInstruction(analyzedInstruction);
2781    }
2782
2783    private void handleExecuteInlineRange(AnalyzedInstruction analyzedInstruction) {
2784        if (deodexUtil == null) {
2785            throw new ValidationException("Cannot analyze an odexed instruction unless we are deodexing");
2786        }
2787
2788        Instruction3rms instruction = (Instruction3rms)analyzedInstruction.instruction;
2789
2790        int methodIndex = instruction.getMethodIndex();
2791        DeodexUtil.InlineMethod inlineMethod = deodexUtil.lookupInlineMethod(methodIndex);
2792        MethodIdItem inlineMethodIdItem = inlineMethod.getMethodIdItem();
2793        if (inlineMethodIdItem == null) {
2794            throw new ValidationException(String.format("Cannot load inline method with index %d", methodIndex));
2795        }
2796
2797        Opcode deodexedOpcode = null;
2798        switch (inlineMethod.methodType) {
2799            case DeodexUtil.Direct:
2800                deodexedOpcode = Opcode.INVOKE_DIRECT;
2801                break;
2802            case DeodexUtil.Static:
2803                deodexedOpcode = Opcode.INVOKE_STATIC;
2804                break;
2805            case DeodexUtil.Virtual:
2806                deodexedOpcode = Opcode.INVOKE_VIRTUAL;
2807                break;
2808            default:
2809                assert false;
2810        }
2811
2812        Instruction3rc deodexedInstruction = new Instruction3rc(deodexedOpcode, instruction.getRegCount(),
2813                instruction.getStartRegister(), inlineMethodIdItem);
2814
2815        analyzedInstruction.setDeodexedInstruction(deodexedInstruction);
2816
2817        analyzeInstruction(analyzedInstruction);
2818    }
2819
2820    private void handleInvokeDirectEmpty(AnalyzedInstruction analyzedInstruction) {
2821        Instruction35s instruction = (Instruction35s)analyzedInstruction.instruction;
2822
2823        Instruction35c deodexedInstruction = new Instruction35c(Opcode.INVOKE_DIRECT, instruction.getRegCount(),
2824                instruction.getRegisterD(), instruction.getRegisterE(), instruction.getRegisterF(),
2825                instruction.getRegisterG(), instruction.getRegisterA(), instruction.getReferencedItem());
2826
2827        analyzedInstruction.setDeodexedInstruction(deodexedInstruction);
2828
2829        analyzeInstruction(analyzedInstruction);
2830    }
2831
2832    private boolean handleIputIgetQuick(AnalyzedInstruction analyzedInstruction, boolean isIput) {
2833        Instruction22cs instruction = (Instruction22cs)analyzedInstruction.instruction;
2834
2835        int fieldOffset = instruction.getFieldOffset();
2836        RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, instruction.getRegisterB(),
2837                ReferenceOrUninitCategories);
2838
2839        if (objectRegisterType.category == RegisterType.Category.Null) {
2840            return false;
2841        }
2842
2843        FieldIdItem fieldIdItem = deodexUtil.lookupField(objectRegisterType.type, fieldOffset);
2844        if (fieldIdItem == null) {
2845            throw new ValidationException(String.format("Could not resolve the field in class %s at offset %d",
2846                    objectRegisterType.type.getClassType(), fieldOffset));
2847        }
2848
2849        String fieldType = fieldIdItem.getFieldType().getTypeDescriptor();
2850
2851        Opcode opcode = getAndCheckIgetIputOpcodeForType(fieldType, instruction.opcode, isIput);
2852
2853        Instruction22c deodexedInstruction = new Instruction22c(opcode, (byte)instruction.getRegisterA(),
2854                (byte)instruction.getRegisterB(), fieldIdItem);
2855        analyzedInstruction.setDeodexedInstruction(deodexedInstruction);
2856
2857        analyzeInstruction(analyzedInstruction);
2858
2859        return true;
2860    }
2861
2862    private boolean handleInvokeVirtualQuick(AnalyzedInstruction analyzedInstruction, boolean isSuper, boolean isRange) {
2863        int methodIndex;
2864        int objectRegister;
2865
2866
2867        if (isRange) {
2868            Instruction3rms instruction = (Instruction3rms)analyzedInstruction.instruction;
2869            methodIndex = instruction.getMethodIndex();
2870            objectRegister = instruction.getStartRegister();
2871        } else {
2872            Instruction35ms instruction = (Instruction35ms)analyzedInstruction.instruction;
2873            methodIndex = instruction.getMethodIndex();
2874            objectRegister = instruction.getRegisterD();
2875        }
2876
2877        RegisterType objectRegisterType = getAndCheckSourceRegister(analyzedInstruction, objectRegister,
2878                ReferenceOrUninitCategories);
2879
2880        if (objectRegisterType.category == RegisterType.Category.Null) {
2881            return false;
2882        }
2883
2884        MethodIdItem methodIdItem = null;
2885        if (isSuper) {
2886            ClassPath.ClassDef classDef = ClassPath.getClassDef(this.encodedMethod.method.getContainingClass(), false);
2887            assert classDef != null;
2888
2889            if (classDef.getSuperclass() != null) {
2890                methodIdItem = deodexUtil.lookupVirtualMethod(classDef.getSuperclass(), methodIndex);
2891            }
2892
2893            if (methodIdItem == null) {
2894                //it's possible that the pre-odexed instruction had used the method from the current class instead
2895                //of from the superclass (although the superclass method is still what would actually be called).
2896                //And so the MethodIdItem for the superclass method may not be in the dex file. Let's try to get the
2897                //MethodIdItem for the method in the current class instead
2898                methodIdItem = deodexUtil.lookupVirtualMethod(classDef, methodIndex);
2899            }
2900        } else{
2901            methodIdItem = deodexUtil.lookupVirtualMethod(objectRegisterType.type, methodIndex);
2902        }
2903
2904        if (methodIdItem == null) {
2905            throw new ValidationException(String.format("Could not resolve the method in class %s at index %d",
2906                    objectRegisterType.type.getClassType(), methodIndex));
2907        }
2908
2909
2910        Instruction deodexedInstruction;
2911        if (isRange) {
2912            Instruction3rms instruction = (Instruction3rms)analyzedInstruction.instruction;
2913            Opcode opcode;
2914            if (isSuper) {
2915                opcode = Opcode.INVOKE_SUPER_RANGE;
2916            } else {
2917                opcode = Opcode.INVOKE_VIRTUAL_RANGE;
2918            }
2919
2920            deodexedInstruction = new Instruction3rc(opcode, instruction.getRegCount(),
2921                    instruction.getStartRegister(), methodIdItem);
2922        } else {
2923            Instruction35ms instruction = (Instruction35ms)analyzedInstruction.instruction;
2924            Opcode opcode;
2925            if (isSuper) {
2926                opcode = Opcode.INVOKE_SUPER;
2927            } else {
2928                opcode = Opcode.INVOKE_VIRTUAL;
2929            }
2930
2931            deodexedInstruction = new Instruction35c(opcode, instruction.getRegCount(),
2932                    instruction.getRegisterD(), instruction.getRegisterE(), instruction.getRegisterF(),
2933                    instruction.getRegisterG(), instruction.getRegisterA(), methodIdItem);
2934        }
2935
2936        analyzedInstruction.setDeodexedInstruction(deodexedInstruction);
2937        analyzeInstruction(analyzedInstruction);
2938
2939        return true;
2940    }
2941
2942    private static Opcode getAndCheckIgetIputOpcodeForType(String fieldType, Opcode odexedOpcode, boolean isIput) {
2943        Opcode opcode;
2944        Opcode validOdexedOpcode;
2945        switch (fieldType.charAt(0)) {
2946            case 'Z':
2947                if (isIput) {
2948                    validOdexedOpcode = Opcode.IPUT_QUICK;
2949                    opcode = Opcode.IPUT_BOOLEAN;
2950                } else {
2951                    validOdexedOpcode = Opcode.IGET_QUICK;
2952                    opcode = Opcode.IGET_BOOLEAN;
2953                }
2954                break;
2955            case 'B':
2956                if (isIput) {
2957                    validOdexedOpcode = Opcode.IPUT_QUICK;
2958                    opcode = Opcode.IPUT_BYTE;
2959                } else {
2960                    validOdexedOpcode = Opcode.IGET_QUICK;
2961                    opcode = Opcode.IGET_BYTE;
2962                }
2963                break;
2964            case 'S':
2965                if (isIput) {
2966                    validOdexedOpcode = Opcode.IPUT_QUICK;
2967                    opcode = Opcode.IPUT_SHORT;
2968                } else {
2969                    validOdexedOpcode = Opcode.IGET_QUICK;
2970                    opcode = Opcode.IGET_SHORT;
2971                }
2972                break;
2973            case 'C':
2974                if (isIput) {
2975                    validOdexedOpcode = Opcode.IPUT_QUICK;
2976                    opcode = Opcode.IPUT_CHAR;
2977                } else {
2978                    validOdexedOpcode = Opcode.IGET_QUICK;
2979                    opcode = Opcode.IGET_CHAR;
2980                }
2981                break;
2982            case 'I':
2983            case 'F':
2984                if (isIput) {
2985                    validOdexedOpcode = Opcode.IPUT_QUICK;
2986                    opcode = Opcode.IPUT;
2987                } else {
2988                    validOdexedOpcode = Opcode.IGET_QUICK;
2989                    opcode = Opcode.IGET;
2990                }
2991                break;
2992            case 'J':
2993            case 'D':
2994                if (isIput) {
2995                    validOdexedOpcode = Opcode.IPUT_WIDE_QUICK;
2996                    opcode = Opcode.IPUT_WIDE;
2997                } else {
2998                    validOdexedOpcode = Opcode.IGET_WIDE_QUICK;
2999                    opcode = Opcode.IGET_WIDE;
3000                }
3001                break;
3002            case 'L':
3003            case '[':
3004                if (isIput) {
3005                    validOdexedOpcode = Opcode.IPUT_OBJECT_QUICK;
3006                    opcode = Opcode.IPUT_OBJECT;
3007                } else {
3008                    validOdexedOpcode = Opcode.IGET_OBJECT_QUICK;
3009                    opcode = Opcode.IGET_OBJECT;
3010                }
3011                break;
3012            default:
3013                throw new RuntimeException(String.format("Unexpected field type %s for %s: ", fieldType,
3014                        odexedOpcode.name));
3015        }
3016
3017        if (odexedOpcode != validOdexedOpcode) {
3018            throw new ValidationException(String.format("Incorrect field type \"%s\" for %s", fieldType,
3019                    odexedOpcode.name));
3020        }
3021
3022        return opcode;
3023    }
3024
3025    private static boolean checkArrayFieldAssignment(RegisterType.Category arrayFieldCategory,
3026                                                  RegisterType.Category instructionCategory) {
3027        if (arrayFieldCategory == instructionCategory) {
3028            return true;
3029        }
3030
3031        if ((arrayFieldCategory == RegisterType.Category.Integer &&
3032             instructionCategory == RegisterType.Category.Float) ||
3033            (arrayFieldCategory == RegisterType.Category.Float &&
3034             instructionCategory == RegisterType.Category.Integer)) {
3035            return true;
3036        }
3037        return false;
3038    }
3039
3040    private static RegisterType getAndCheckSourceRegister(AnalyzedInstruction analyzedInstruction, int registerNumber,
3041                                            EnumSet validCategories) {
3042        assert registerNumber >= 0 && registerNumber < analyzedInstruction.postRegisterMap.length;
3043
3044        RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(registerNumber);
3045        assert registerType != null;
3046
3047        checkRegister(registerType, registerNumber, validCategories);
3048
3049        if (validCategories == WideLowCategories) {
3050            checkRegister(registerType, registerNumber, WideLowCategories);
3051            checkWidePair(registerNumber, analyzedInstruction);
3052
3053            RegisterType secondRegisterType = analyzedInstruction.getPreInstructionRegisterType(registerNumber + 1);
3054            assert secondRegisterType != null;
3055            checkRegister(secondRegisterType, registerNumber+1, WideHighCategories);
3056        }
3057
3058        return registerType;
3059    }
3060
3061    private static void checkRegister(RegisterType registerType, int registerNumber, EnumSet validCategories) {
3062        if (!validCategories.contains(registerType.category)) {
3063            throw new ValidationException(String.format("Invalid register type %s for register v%d.",
3064                    registerType.toString(), registerNumber));
3065        }
3066    }
3067
3068    private static void checkWidePair(int registerNumber, AnalyzedInstruction analyzedInstruction) {
3069        if (registerNumber + 1 >= analyzedInstruction.postRegisterMap.length) {
3070            throw new ValidationException(String.format("v%d is the last register and not a valid wide register " +
3071                    "pair.", registerNumber));
3072        }
3073    }
3074}
3075