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