MethodAnalyzer.java revision 2d6d6eb22c86c79344b85ffe46fdd4268566bd0f
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    //This is a dummy instruction that occurs immediately before the first real instruction. We can initialize the
19    //register types for this instruction to the parameter types, in order to have them propagate to all of its
20    //successors, e.g. the first real instruction, the first instructions in any exception handlers covering the first
21    //instruction, etc.
22    private AnalyzedInstruction startOfMethod;
23
24    public MethodAnalyzer(ClassDataItem.EncodedMethod encodedMethod) {
25        if (encodedMethod == null) {
26            throw new IllegalArgumentException("encodedMethod cannot be null");
27        }
28        if (encodedMethod.codeItem == null || encodedMethod.codeItem.getInstructions().length == 0) {
29            throw new IllegalArgumentException("The method has no code");
30        }
31        this.encodedMethod = encodedMethod;
32        buildInstructionList();
33
34        //override AnalyzedInstruction and provide custom implementations of some of the methods, so that we don't
35        //have to handle the case this special case of instruction being null, in the main class
36        startOfMethod = new AnalyzedInstruction(null, -1, encodedMethod.codeItem.getRegisterCount()) {
37            public boolean setsRegister() {
38                return false;
39            }
40
41            @Override
42            public boolean setsWideRegister() {
43                return false;
44            }
45
46            @Override
47            public boolean setsRegister(int registerNumber) {
48                return false;
49            }
50
51            @Override
52            public int getDestinationRegister() {
53                assert false;
54                return -1;
55            };
56        };
57    }
58
59    public AnalyzedInstruction[] analyze() {
60        assert encodedMethod != null;
61        assert encodedMethod.codeItem != null;
62
63        if (analyzed) {
64            return makeInstructionArray();
65        }
66
67        CodeItem codeItem = encodedMethod.codeItem;
68        MethodIdItem methodIdItem = encodedMethod.method;
69
70        int totalRegisters = codeItem.getRegisterCount();
71        int parameterRegisters = methodIdItem.getPrototype().getParameterRegisterCount();
72
73        //if this isn't a static method, determine which register is the "this" register and set the type to the
74        //current class
75        if ((encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) == 0) {
76            int thisRegister = totalRegisters - parameterRegisters - 1;
77
78            //if this is a constructor, then set the "this" register to an uninitialized reference of the current class
79            if ((encodedMethod.accessFlags & AccessFlags.CONSTRUCTOR.getValue()) != 0) {
80                //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
81                if (!encodedMethod.method.getMethodName().equals("<init>")) {
82                    throw new ValidationException("The constructor flag can only be used with an <init> method.");
83                }
84
85                setRegisterTypeAndPropagateChanges(startOfMethod, thisRegister,
86                        RegisterType.getRegisterType(RegisterType.Category.UninitRef,
87                            ClassPath.getClassDef(methodIdItem.getContainingClass())));
88            } else {
89                if (encodedMethod.method.getMethodName().equals("<init>")) {
90                    throw new ValidationException("An <init> method must have the \"constructor\" access flag");
91                }
92
93                setRegisterTypeAndPropagateChanges(startOfMethod, thisRegister,
94                        RegisterType.getRegisterType(RegisterType.Category.Reference,
95                            ClassPath.getClassDef(methodIdItem.getContainingClass())));
96            }
97        }
98
99        TypeListItem parameters = methodIdItem.getPrototype().getParameters();
100        if (parameters != null) {
101            RegisterType[] parameterTypes = getParameterTypes(parameters, parameterRegisters);
102            for (int i=0; i<parameterTypes.length; i++) {
103                RegisterType registerType = parameterTypes[i];
104                int registerNum = (totalRegisters - parameterRegisters) + i;
105                setRegisterTypeAndPropagateChanges(startOfMethod, registerNum, registerType);
106            }
107        }
108
109        analyzed = true;
110        return makeInstructionArray();
111    }
112
113    private int getThisRegister() {
114        assert (encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) == 0;
115
116        CodeItem codeItem = encodedMethod.codeItem;
117        assert codeItem != null;
118
119        MethodIdItem methodIdItem = encodedMethod.method;
120        assert methodIdItem != null;
121
122        int totalRegisters = codeItem.getRegisterCount();
123        if (totalRegisters == 0) {
124            throw new ValidationException("A non-static method must have at least 1 register");
125        }
126
127        int parameterRegisters = methodIdItem.getPrototype().getParameterRegisterCount();
128
129        return totalRegisters - parameterRegisters - 1;
130    }
131
132    private boolean isInstanceConstructor() {
133        return (encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) == 0 &&
134               (encodedMethod.accessFlags & AccessFlags.CONSTRUCTOR.getValue()) != 0;
135    }
136
137    private boolean isStaticConstructor() {
138        return (encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) != 0 &&
139               (encodedMethod.accessFlags & AccessFlags.CONSTRUCTOR.getValue()) != 0;
140    }
141
142    public AnalyzedInstruction[] makeInstructionArray() {
143        AnalyzedInstruction[] instructionArray = new AnalyzedInstruction[instructions.size()];
144        for (int i=0; i<instructions.size(); i++) {
145            instructionArray[i] = instructions.valueAt(i);
146        }
147        return instructionArray;
148    }
149
150    private static RegisterType[] getParameterTypes(TypeListItem typeListItem, int parameterRegisterCount) {
151        assert typeListItem != null;
152        assert parameterRegisterCount == typeListItem.getRegisterCount();
153
154        RegisterType[] registerTypes = new RegisterType[parameterRegisterCount];
155
156        int registerNum = 0;
157        for (TypeIdItem type: typeListItem.getTypes()) {
158            if (type.getRegisterCount() == 2) {
159                registerTypes[registerNum++] = RegisterType.getWideRegisterTypeForTypeIdItem(type, true);
160                registerTypes[registerNum++] = RegisterType.getWideRegisterTypeForTypeIdItem(type, false);
161            } else {
162                registerTypes[registerNum] = RegisterType.getRegisterTypeForTypeIdItem(type);
163            }
164        }
165
166        return registerTypes;
167    }
168
169    private int getInstructionAddress(AnalyzedInstruction instruction) {
170        return instructions.keyAt(instruction.instructionIndex);
171    }
172
173    private void setWideDestinationRegisterTypeAndPropagateChanges(AnalyzedInstruction analyzedInstruction,
174                                                                   RegisterType registerType) {
175        assert registerType.category == RegisterType.Category.LongLo ||
176               registerType.category == RegisterType.Category.DoubleLo;
177
178        checkWideDestinationPair(analyzedInstruction);
179
180        setRegisterTypeAndPropagateChanges(analyzedInstruction, analyzedInstruction.getDestinationRegister(),
181                registerType);
182        if (registerType.category == RegisterType.Category.LongLo) {
183            setRegisterTypeAndPropagateChanges(analyzedInstruction, analyzedInstruction.getDestinationRegister() + 1,
184                RegisterType.getRegisterType(RegisterType.Category.LongHi, null));
185        } else {
186            setRegisterTypeAndPropagateChanges(analyzedInstruction, analyzedInstruction.getDestinationRegister() + 1,
187                RegisterType.getRegisterType(RegisterType.Category.DoubleHi, null));
188        }
189    }
190
191    private void setDestinationRegisterTypeAndPropagateChanges(AnalyzedInstruction analyzedInstruction,
192                                                               RegisterType registerType) {
193        setRegisterTypeAndPropagateChanges(analyzedInstruction, analyzedInstruction.getDestinationRegister(),
194                registerType);
195    }
196
197    private void setRegisterTypeAndPropagateChanges(AnalyzedInstruction instruction, int registerNumber,
198                                                RegisterType registerType) {
199
200        BitSet changedInstructions = new BitSet(instructions.size());
201
202        boolean changed = instruction.setPostRegisterType(registerNumber, registerType);
203
204        if (!changed || instruction.setsRegister(registerNumber)) {
205            return;
206        }
207
208        propagateRegisterToSuccessors(instruction, registerNumber, changedInstructions);
209
210        //using a for loop inside the while loop optimizes for the common case of the successors of an instruction
211        //occurring after the instruction. Any successors that occur prior to the instruction will be picked up on
212        //the next iteration of the while loop.
213        //this could also be done recursively, but in large methods it would likely cause very deep recursion,
214        //which would requires the user to specify a larger stack size. This isn't really a problem, but it is
215        //slightly annoying.
216        while (!changedInstructions.isEmpty()) {
217            for (int instructionIndex=changedInstructions.nextSetBit(0);
218                     instructionIndex>=0;
219                     instructionIndex=changedInstructions.nextSetBit(instructionIndex)) {
220
221                changedInstructions.clear(instructionIndex);
222
223                propagateRegisterToSuccessors(instructions.valueAt(instructionIndex), registerNumber,
224                        changedInstructions);
225            }
226        }
227    }
228
229    private void propagateRegisterToSuccessors(AnalyzedInstruction instruction, int registerNumber,
230                                               BitSet changedInstructions) {
231        for (AnalyzedInstruction successor: instruction.successors) {
232            if (!successor.setsRegister(registerNumber)) {
233                RegisterType registerType = successor.getMergedRegisterTypeFromPredecessors(registerNumber);
234
235                if (successor.setPostRegisterType(registerNumber, registerType)) {
236                    changedInstructions.set(successor.instructionIndex);
237                }
238            }
239        }
240    }
241
242
243
244    private void buildInstructionList() {
245        assert encodedMethod != null;
246        assert encodedMethod.codeItem != null;
247        int registerCount = encodedMethod.codeItem.getRegisterCount();
248
249        startOfMethod = new AnalyzedInstruction(null, -1, registerCount);
250
251        Instruction[] insns = encodedMethod.codeItem.getInstructions();
252
253        instructions = new SparseArray<AnalyzedInstruction>(insns.length);
254
255        //first, create all the instructions and populate the instructionAddresses array
256        int currentCodeAddress = 0;
257        for (int i=0; i<insns.length; i++) {
258            instructions.append(currentCodeAddress, new AnalyzedInstruction(insns[i], i, registerCount));
259            assert instructions.indexOfKey(currentCodeAddress) == i;
260            currentCodeAddress += insns[i].getSize(currentCodeAddress);
261        }
262
263        //next, populate the exceptionHandlers array. The array item for each instruction that can throw an exception
264        //and is covered by a try block should be set to a list of the first instructions of each exception handler
265        //for the try block covering the instruction
266        CodeItem.TryItem[] tries = encodedMethod.codeItem.getTries();
267        int triesIndex = 0;
268        CodeItem.TryItem currentTry = null;
269        AnalyzedInstruction[] currentExceptionHandlers = null;
270        AnalyzedInstruction[][] exceptionHandlers = new AnalyzedInstruction[insns.length][];
271
272        for (int i=0; i<instructions.size(); i++) {
273            AnalyzedInstruction instruction = instructions.valueAt(i);
274            Opcode instructionOpcode = instruction.instruction.opcode;
275
276            //check if we have gone past the end of the current try
277            if (currentTry != null) {
278                if (currentTry.getStartCodeAddress() + currentTry.getTryLength() <= currentCodeAddress) {
279                    currentTry = null;
280                    triesIndex++;
281                }
282            }
283
284            //check if the next try is applicable yet
285            if (currentTry == null && triesIndex < tries.length) {
286                CodeItem.TryItem tryItem = tries[triesIndex];
287                if (tryItem.getStartCodeAddress() <= currentCodeAddress) {
288                    assert(tryItem.getStartCodeAddress() + tryItem.getTryLength() > currentCodeAddress);
289
290                    currentTry = tryItem;
291
292                    currentExceptionHandlers = buildExceptionHandlerArray(tryItem);
293                }
294            }
295
296            //if we're inside a try block, and the instruction can throw an exception, then add the exception handlers
297            //for the current instruction
298            if (currentTry != null && instructionOpcode.canThrow()) {
299                exceptionHandlers[i] = currentExceptionHandlers;
300            }
301        }
302
303        //finally, populate the successors and predecessors for each instruction
304        assert instructions.size() > 0;
305        addPredecessorSuccessor(startOfMethod, instructions.valueAt(0), exceptionHandlers);
306        startOfMethod.addSuccessor(instructions.valueAt(0));
307
308        for (int i=0; i<instructions.size(); i++) {
309            AnalyzedInstruction instruction = instructions.valueAt(i);
310            Opcode instructionOpcode = instruction.instruction.opcode;
311            int instructionCodeAddress = getInstructionAddress(instruction);
312
313            if (instruction.instruction.opcode.canContinue()) {
314                if (i == instructions.size() - 1) {
315                    throw new ValidationException("Execution can continue past the last instruction");
316                }
317                AnalyzedInstruction nextInstruction = instructions.valueAt(i+1);
318                addPredecessorSuccessor(instruction, nextInstruction, exceptionHandlers);
319            }
320
321            if (instruction instanceof OffsetInstruction) {
322                OffsetInstruction offsetInstruction = (OffsetInstruction)instruction;
323
324                if (instructionOpcode == Opcode.PACKED_SWITCH || instructionOpcode == Opcode.SPARSE_SWITCH) {
325                    MultiOffsetInstruction switchDataInstruction =
326                            (MultiOffsetInstruction)instructions.get(instructionCodeAddress +
327                                    offsetInstruction.getTargetAddressOffset()).instruction;
328                    for (int targetAddressOffset: switchDataInstruction.getTargets()) {
329                        AnalyzedInstruction targetInstruction = instructions.get(instructionCodeAddress +
330                                targetAddressOffset);
331
332                        addPredecessorSuccessor(instruction, targetInstruction, exceptionHandlers);
333                    }
334                } else {
335                    int targetAddressOffset = offsetInstruction.getTargetAddressOffset();
336                    AnalyzedInstruction targetInstruction = instructions.get(instructionCodeAddress +
337                            targetAddressOffset);
338                    addPredecessorSuccessor(instruction, targetInstruction, exceptionHandlers);
339                }
340            }
341        }
342    }
343
344    private void addPredecessorSuccessor(AnalyzedInstruction predecessor, AnalyzedInstruction successor,
345                                                AnalyzedInstruction[][] exceptionHandlers) {
346        addPredecessorSuccessor(predecessor, successor, exceptionHandlers, false);
347    }
348
349    private void addPredecessorSuccessor(AnalyzedInstruction predecessor, AnalyzedInstruction successor,
350                                                AnalyzedInstruction[][] exceptionHandlers, boolean allowMoveException) {
351
352        if (!allowMoveException && successor.instruction.opcode == Opcode.MOVE_EXCEPTION) {
353            throw new ValidationException("Execution can pass from the " + predecessor.instruction.opcode.name +
354                    " instruction at code address 0x" + Integer.toHexString(getInstructionAddress(predecessor)) +
355                    " to the move-exception instruction at address 0x" +
356                    Integer.toHexString(getInstructionAddress(successor)));
357        }
358
359        if (!predecessor.addSuccessor(successor)) {
360            //if predecessor already had successor as a successor, then there's nothing else to do
361            return;
362        }
363
364        successor.addPredecessor(predecessor);
365
366        //TODO: need to handle the case of monitor-exit as a special case - the exception is thrown *after* the instruction executes
367        //if the successor can throw an instruction, then we need to add the exception handlers as additional
368        //successors to the predecessor (and then apply this same logic recursively if needed)
369        AnalyzedInstruction[] exceptionHandlersForSuccessor = exceptionHandlers[successor.instructionIndex];
370        if (exceptionHandlersForSuccessor != null) {
371            //the item for this instruction in exceptionHandlersForSuccessor should only be set if this instruction
372            //can throw an exception
373            assert predecessor.instruction.opcode.canThrow();
374
375            for (AnalyzedInstruction exceptionHandler: exceptionHandlersForSuccessor) {
376                addPredecessorSuccessor(predecessor, exceptionHandler, exceptionHandlers, true);
377            }
378        }
379    }
380
381    private AnalyzedInstruction[] buildExceptionHandlerArray(CodeItem.TryItem tryItem) {
382        int exceptionHandlerCount = tryItem.encodedCatchHandler.handlers.length;
383        int catchAllHandler = tryItem.encodedCatchHandler.getCatchAllHandlerAddress();
384        if (catchAllHandler != -1) {
385            exceptionHandlerCount++;
386        }
387
388        AnalyzedInstruction[] exceptionHandlers = new AnalyzedInstruction[exceptionHandlerCount];
389        for (int i=0; i<tryItem.encodedCatchHandler.handlers.length; i++) {
390            exceptionHandlers[i] = instructions.get(tryItem.encodedCatchHandler.handlers[i].getHandlerAddress());
391        }
392
393        if (catchAllHandler != -1) {
394            exceptionHandlers[exceptionHandlers.length - 1] = instructions.get(catchAllHandler);
395        }
396
397        return exceptionHandlers;
398    }
399
400    private boolean setDestinationRegisterTypeForInstruction(AnalyzedInstruction analyzedInstruction) {
401        Instruction instruction = analyzedInstruction.instruction;
402
403        switch (instruction.opcode) {
404            case NOP:
405                return true;
406            case MOVE:
407            case MOVE_FROM16:
408            case MOVE_16:
409                return handleMove(analyzedInstruction, Primitive32BitCategories);
410            case MOVE_WIDE:
411            case MOVE_WIDE_FROM16:
412            case MOVE_WIDE_16:
413                return handleMoveWide(analyzedInstruction);
414            case MOVE_OBJECT:
415            case MOVE_OBJECT_FROM16:
416            case MOVE_OBJECT_16:
417                return handleMove(analyzedInstruction, ReferenceCategories);
418            case MOVE_RESULT:
419                return handleMoveResult(analyzedInstruction, Primitive32BitCategories);
420            case MOVE_RESULT_WIDE:
421                return handleMoveResult(analyzedInstruction, WideLowCategories);
422            case MOVE_RESULT_OBJECT:
423                return handleMoveResult(analyzedInstruction, ReferenceCategories);
424            case MOVE_EXCEPTION:
425                return handleMoveException(analyzedInstruction);
426            case RETURN_VOID:
427                return handleReturnVoid(analyzedInstruction);
428            case RETURN:
429                return handleReturn(analyzedInstruction);
430            case RETURN_WIDE:
431                return handleReturnWide(analyzedInstruction);
432            case RETURN_OBJECT:
433                return handleReturnObject(analyzedInstruction);
434            case CONST_4:
435            case CONST_16:
436            case CONST:
437                return handleConst(analyzedInstruction);
438            case CONST_HIGH16:
439                return handleConstHigh16(analyzedInstruction);
440            case CONST_WIDE_16:
441            case CONST_WIDE_32:
442            case CONST_WIDE:
443            case CONST_WIDE_HIGH16:
444                return handleWideConst(analyzedInstruction);
445            case CONST_STRING:
446            case CONST_STRING_JUMBO:
447                return handleConstString(analyzedInstruction);
448            case CONST_CLASS:
449                return handleConstClass(analyzedInstruction);
450            case MONITOR_ENTER:
451            case MONITOR_EXIT:
452                return handleMonitor(analyzedInstruction);
453            case CHECK_CAST:
454                return handleCheckCast(analyzedInstruction);
455            case INSTANCE_OF:
456                return handleInstanceOf(analyzedInstruction);
457            case ARRAY_LENGTH:
458                return handleArrayLength(analyzedInstruction);
459            case NEW_INSTANCE:
460                return handleNewInstance(analyzedInstruction);
461            case NEW_ARRAY:
462                return handleNewArray(analyzedInstruction);
463            case FILLED_NEW_ARRAY:
464                return handleFilledNewArray(analyzedInstruction);
465            case FILLED_NEW_ARRAY_RANGE:
466                return handleFilledNewArrayRange(analyzedInstruction);
467            case FILL_ARRAY_DATA:
468                return handleFillArrayData(analyzedInstruction);
469            case THROW:
470                return handleThrow(analyzedInstruction);
471            case GOTO:
472            case GOTO_16:
473            case GOTO_32:
474                //nothing to do
475                return true;
476            case PACKED_SWITCH:
477                return handleSwitch(analyzedInstruction, Format.PackedSwitchData);
478            case SPARSE_SWITCH:
479                return handleSwitch(analyzedInstruction, Format.SparseSwitchData);
480            case CMPL_FLOAT:
481            case CMPG_FLOAT:
482                return handleFloatCmp(analyzedInstruction);
483            case CMPL_DOUBLE:
484            case CMPG_DOUBLE:
485            case CMP_LONG:
486                return handleWideCmp(analyzedInstruction);
487            case IF_EQ:
488            case IF_NE:
489                return handleIfEqNe(analyzedInstruction);
490            case IF_LT:
491            case IF_GE:
492            case IF_GT:
493            case IF_LE:
494                return handleIf(analyzedInstruction);
495            case IF_EQZ:
496            case IF_NEZ:
497                return handleIfEqzNez(analyzedInstruction);
498            case IF_LTZ:
499            case IF_GEZ:
500            case IF_GTZ:
501            case IF_LEZ:
502                return handleIfz(analyzedInstruction);
503            case AGET:
504                return handle32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Integer);
505            case AGET_BOOLEAN:
506                return handle32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Boolean);
507            case AGET_BYTE:
508                return handle32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Byte);
509            case AGET_CHAR:
510                return handle32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Char);
511            case AGET_SHORT:
512                return handle32BitPrimitiveAget(analyzedInstruction, RegisterType.Category.Short);
513            case AGET_WIDE:
514                return handleAgetWide(analyzedInstruction);
515            case AGET_OBJECT:
516                return handleAgetObject(analyzedInstruction);
517            case APUT:
518                return handle32BitPrimitiveAput(analyzedInstruction, RegisterType.Category.Integer);
519            case APUT_BOOLEAN:
520                return handle32BitPrimitiveAput(analyzedInstruction, RegisterType.Category.Boolean);
521            case APUT_BYTE:
522                return handle32BitPrimitiveAput(analyzedInstruction, RegisterType.Category.Byte);
523            case APUT_CHAR:
524                return handle32BitPrimitiveAput(analyzedInstruction, RegisterType.Category.Char);
525            case APUT_SHORT:
526                return handle32BitPrimitiveAput(analyzedInstruction, RegisterType.Category.Short);
527            case APUT_WIDE:
528                return handleAputWide(analyzedInstruction);
529            case APUT_OBJECT:
530                return handleAputObject(analyzedInstruction);
531            case IGET:
532                return handle32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Integer);
533            case IGET_BOOLEAN:
534                return handle32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Boolean);
535            case IGET_BYTE:
536                return handle32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Byte);
537            case IGET_CHAR:
538                return handle32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Char);
539            case IGET_SHORT:
540                return handle32BitPrimitiveIget(analyzedInstruction, RegisterType.Category.Short);
541            case IGET_WIDE:
542                return handleIgetWide(analyzedInstruction);
543            case IGET_OBJECT:
544                return handleIgetObject(analyzedInstruction);
545            case IPUT:
546                return handle32BitPrimitiveIput(analyzedInstruction, RegisterType.Category.Integer);
547            case IPUT_BOOLEAN:
548                return handle32BitPrimitiveIput(analyzedInstruction, RegisterType.Category.Boolean);
549            case IPUT_BYTE:
550                return handle32BitPrimitiveIput(analyzedInstruction, RegisterType.Category.Byte);
551            case IPUT_CHAR:
552                return handle32BitPrimitiveIput(analyzedInstruction, RegisterType.Category.Char);
553            case IPUT_SHORT:
554                return handle32BitPrimitiveIput(analyzedInstruction, RegisterType.Category.Short);
555            case IPUT_WIDE:
556                return handleIputWide(analyzedInstruction);
557            case IPUT_OBJECT:
558                return handleIputObject(analyzedInstruction);
559            case SGET:
560                return handle32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Integer);
561            case SGET_BOOLEAN:
562                return handle32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Boolean);
563            case SGET_BYTE:
564                return handle32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Byte);
565            case SGET_CHAR:
566                return handle32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Char);
567            case SGET_SHORT:
568                return handle32BitPrimitiveSget(analyzedInstruction, RegisterType.Category.Short);
569            case SGET_WIDE:
570                return handleSgetWide(analyzedInstruction);
571            case SGET_OBJECT:
572                return handleSgetObject(analyzedInstruction);
573        }
574
575        assert false;
576        return false;
577    }
578
579    private static final EnumSet<RegisterType.Category> Primitive32BitCategories = EnumSet.of(
580            RegisterType.Category.Null,
581            RegisterType.Category.Boolean,
582            RegisterType.Category.Byte,
583            RegisterType.Category.Short,
584            RegisterType.Category.Char,
585            RegisterType.Category.Integer,
586            RegisterType.Category.Float);
587
588    private static final EnumSet<RegisterType.Category> WideLowCategories = EnumSet.of(
589            RegisterType.Category.LongLo,
590            RegisterType.Category.DoubleLo);
591
592    private static final EnumSet<RegisterType.Category> WideHighCategories = EnumSet.of(
593            RegisterType.Category.LongHi,
594            RegisterType.Category.DoubleHi);
595
596    private static final EnumSet<RegisterType.Category> ReferenceCategories = EnumSet.of(
597            RegisterType.Category.Null,
598            RegisterType.Category.Reference);
599
600    private boolean handleMove(AnalyzedInstruction analyzedInstruction,
601                               EnumSet<RegisterType.Category> allowedCategories) {
602        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
603
604        //get the "pre-instruction" register type for the source register
605        RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
606        assert sourceRegisterType != null;
607
608        if (sourceRegisterType.category == RegisterType.Category.Unknown) {
609            //we don't know the source register type yet, so we can't verify it. Return false, and we'll come back later
610            return false;
611        }
612
613        checkRegister(sourceRegisterType, allowedCategories);
614
615        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, sourceRegisterType);
616        return true;
617    }
618
619    private boolean handleMoveWide(AnalyzedInstruction analyzedInstruction) {
620        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
621
622        RegisterType sourceRegisterType = getAndCheckWideSourcePair(analyzedInstruction,
623                instruction.getRegisterB());
624        assert sourceRegisterType != null;
625
626        if (sourceRegisterType.category == RegisterType.Category.Unknown) {
627            //we don't know the source register type yet, so we can't verify it. Return false, and we'll come back later
628            return false;
629        }
630
631        checkWideDestinationPair(analyzedInstruction);
632
633        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, sourceRegisterType);
634        return true;
635    }
636
637    private boolean handleMoveResult(AnalyzedInstruction analyzedInstruction,
638                                     EnumSet<RegisterType.Category> allowedCategories) {
639
640        //TODO: handle the case when the previous instruction is an odexed instruction
641
642        if (analyzedInstruction.instructionIndex == 0) {
643            throw new ValidationException(analyzedInstruction.instruction.opcode.name + " cannot be the first " +
644                    "instruction in a method. It must occur after an invoke-*/fill-new-array instruction");
645        }
646
647        AnalyzedInstruction previousInstruction = instructions.valueAt(analyzedInstruction.instructionIndex-1);
648
649        if (!previousInstruction.instruction.opcode.setsResult()) {
650            throw new ValidationException(analyzedInstruction.instruction.opcode.name + " must occur after an " +
651                    "invoke-*/fill-new-array instruction");
652        }
653
654        if (analyzedInstruction.instruction.opcode.setsWideRegister()) {
655            checkWideDestinationPair(analyzedInstruction);
656        }
657
658        //TODO: does dalvik allow a move-result after an invoke with a void return type?
659        RegisterType destinationRegisterType;
660
661        InstructionWithReference invokeInstruction = (InstructionWithReference)previousInstruction.instruction;
662        Item item = invokeInstruction.getReferencedItem();
663
664        if (item instanceof MethodIdItem) {
665            destinationRegisterType = RegisterType.getRegisterTypeForTypeIdItem(
666                    ((MethodIdItem)item).getPrototype().getReturnType());
667        } else {
668            assert item instanceof TypeIdItem;
669            destinationRegisterType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item);
670        }
671
672        checkRegister(destinationRegisterType, allowedCategories);
673        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, destinationRegisterType);
674        return true;
675    }
676
677    private boolean handleMoveException(AnalyzedInstruction analyzedInstruction) {
678        CodeItem.TryItem[] tries = encodedMethod.codeItem.getTries();
679        int instructionAddress = getInstructionAddress(analyzedInstruction);
680
681        if (tries == null) {
682            throw new ValidationException("move-exception must be the first instruction in an exception handler block");
683        }
684
685        RegisterType exceptionType = null;
686
687        for (CodeItem.TryItem tryItem: encodedMethod.codeItem.getTries()) {
688            if (tryItem.encodedCatchHandler.getCatchAllHandlerAddress() == instructionAddress) {
689                exceptionType = RegisterType.getRegisterType(RegisterType.Category.Reference,
690                        ClassPath.getClassDef("Ljava/lang/Throwable;"));
691                break;
692            }
693            for (CodeItem.EncodedTypeAddrPair handler: tryItem.encodedCatchHandler.handlers) {
694                if (handler.getHandlerAddress() == instructionAddress) {
695                    exceptionType = RegisterType.getRegisterTypeForTypeIdItem(handler.exceptionType)
696                            .merge(exceptionType);
697                }
698            }
699        }
700
701        //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)
702        checkRegister(exceptionType, ReferenceCategories);
703        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, exceptionType);
704        return true;
705    }
706
707    private boolean checkConstructorReturn(AnalyzedInstruction analyzedInstruction) {
708        assert this.isInstanceConstructor();
709
710        //if we're in an instance constructor (an <init> method), then the superclass <init> must have been called.
711        //When execution enters the method, the "this" register is set as an uninitialized reference to the containing
712        //class. Once the superclass' <init> is called, the "this" register is upgraded to a full-blown reference type,
713        //so we need to ensure that the "this" register isn't an uninitialized reference
714
715        int thisRegister = getThisRegister();
716        RegisterType thisRegisterType = analyzedInstruction.postRegisterMap[thisRegister];
717
718        if (thisRegisterType.category == RegisterType.Category.Unknown) {
719            //we don't have enough information yet, so return false. We'll come back later
720            return false;
721        }
722        if (thisRegisterType.category == RegisterType.Category.UninitRef) {
723            throw new ValidationException("Returning from constructor without calling the superclass' <init>");
724        }
725        assert thisRegisterType.category == RegisterType.Category.Reference;
726        assert thisRegisterType.type == ClassPath.getClassDef(encodedMethod.method.getContainingClass());
727        return true;
728    }
729
730    private boolean handleReturnVoid(AnalyzedInstruction analyzedInstruction) {
731        if (this.isInstanceConstructor()) {
732            if (!checkConstructorReturn(analyzedInstruction)) {
733                return false;
734            }
735        }
736
737        TypeIdItem returnType = encodedMethod.method.getPrototype().getReturnType();
738        if (returnType.getTypeDescriptor().charAt(0) != 'V') {
739            //TODO: could add which return-* variation should be used instead
740            throw new ValidationException("Cannot use return-void with a non-void return type (" +
741                returnType.getTypeDescriptor() + ")");
742        }
743        return true;
744    }
745
746    private boolean handleReturn(AnalyzedInstruction analyzedInstruction) {
747        if (this.isInstanceConstructor()) {
748            if (!checkConstructorReturn(analyzedInstruction)) {
749                return false;
750            }
751        }
752
753        SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
754        RegisterType returnRegisterType = analyzedInstruction.postRegisterMap[instruction.getRegisterA()];
755
756        if (returnRegisterType.category == RegisterType.Category.Unknown) {
757            return false;
758        }
759
760        checkRegister(returnRegisterType, Primitive32BitCategories);
761
762        TypeIdItem returnType = encodedMethod.method.getPrototype().getReturnType();
763        if (returnType.getTypeDescriptor().charAt(0) == 'V') {
764            throw new ValidationException("Cannot use return with a void return type. Use return-void instead");
765        }
766
767        RegisterType registerType = RegisterType.getRegisterTypeForTypeIdItem(returnType);
768
769        if (!Primitive32BitCategories.contains(registerType.category)) {
770            //TODO: could add which return-* variation should be used instead
771            throw new ValidationException("Cannot use return with return type " + returnType.getTypeDescriptor());
772        }
773
774
775        return true;
776    }
777
778    private boolean handleReturnWide(AnalyzedInstruction analyzedInstruction) {
779        if (this.isInstanceConstructor()) {
780            if (!checkConstructorReturn(analyzedInstruction)) {
781                return false;
782            }
783        }
784
785        SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
786        RegisterType returnType = getAndCheckWideSourcePair(analyzedInstruction, instruction.getRegisterA());
787
788        if (returnType.category == RegisterType.Category.Unknown) {
789            return false;
790        }
791
792
793        TypeIdItem returnTypeIdItem = encodedMethod.method.getPrototype().getReturnType();
794        if (returnTypeIdItem.getTypeDescriptor().charAt(0) == 'V') {
795            throw new ValidationException("Cannot use return-wide with a void return type. Use return-void instead");
796        }
797
798        returnType = RegisterType.getRegisterTypeForTypeIdItem(returnTypeIdItem);
799        if (!WideLowCategories.contains(returnType.category)) {
800            //TODO: could add which return-* variation should be used instead
801            throw new ValidationException("Cannot use return-wide with return type " +
802                    returnTypeIdItem.getTypeDescriptor());
803        }
804
805        return true;
806    }
807
808    private boolean handleReturnObject(AnalyzedInstruction analyzedInstruction) {
809        if (this.isInstanceConstructor()) {
810            if (!checkConstructorReturn(analyzedInstruction)) {
811                return false;
812            }
813        }
814
815        SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
816        int returnRegister = instruction.getRegisterA();
817        RegisterType returnRegisterType = analyzedInstruction.postRegisterMap[returnRegister];
818
819        if (returnRegisterType.category == RegisterType.Category.Unknown) {
820            return false;
821        }
822
823        checkRegister(returnRegisterType, ReferenceCategories);
824
825
826        TypeIdItem returnTypeIdItem = encodedMethod.method.getPrototype().getReturnType();
827        if (returnTypeIdItem.getTypeDescriptor().charAt(0) == 'V') {
828            throw new ValidationException("Cannot use return with a void return type. Use return-void instead");
829        }
830
831        RegisterType returnType = RegisterType.getRegisterTypeForTypeIdItem(returnTypeIdItem);
832
833        if (!ReferenceCategories.contains(returnType.category)) {
834            //TODO: could add which return-* variation should be used instead
835            throw new ValidationException("Cannot use " + analyzedInstruction + " with return type " +
836                    returnTypeIdItem.getTypeDescriptor());
837        }
838
839        if (returnType.type.isInterface()) {
840            if (!returnRegisterType.type.implementsInterface(returnType.type)) {
841                //TODO: how to handle warnings?
842            }
843        } else {
844            if (!returnRegisterType.type.extendsClass(returnType.type)) {
845                throw new ValidationException("The return value in register v" + Integer.toString(returnRegister) +
846                        "(" + returnRegisterType.type.getClassType() + ") is not compatible with the method's return " +
847                        "type (" + returnType.type.getClassType() + ")");
848            }
849        }
850
851        return true;
852    }
853
854    private boolean handleConst(AnalyzedInstruction analyzedInstruction) {
855        LiteralInstruction instruction = (LiteralInstruction)analyzedInstruction.instruction;
856
857        RegisterType newDestinationRegisterType = RegisterType.getRegisterTypeForLiteral(instruction.getLiteral());
858
859        //we assume that the literal value is a valid value for the given instruction type, because it's impossible
860        //to store an invalid literal with the instruction. so we don't need to check the type of the literal
861        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, newDestinationRegisterType);
862        return true;
863    }
864
865    private boolean handleConstHigh16(AnalyzedInstruction analyzedInstruction) {
866        LiteralInstruction instruction = (LiteralInstruction)analyzedInstruction.instruction;
867
868        //TODO: test this
869        long literalValue = instruction.getLiteral() << 16;
870        RegisterType newDestinationRegisterType = RegisterType.getRegisterTypeForLiteral(literalValue);
871
872        //we assume that the literal value is a valid value for the given instruction type, because it's impossible
873        //to store an invalid literal with the instruction. so we don't need to check the type of the literal
874        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, newDestinationRegisterType);
875        return true;
876    }
877
878    private boolean handleWideConst(AnalyzedInstruction analyzedInstruction) {
879        setWideDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
880                RegisterType.getRegisterType(RegisterType.Category.LongLo, null));
881        return true;
882    }
883
884    private boolean handleConstString(AnalyzedInstruction analyzedInstruction) {
885        ClassPath.ClassDef stringClassDef = ClassPath.getClassDef("Ljava/lang/String;");
886        RegisterType stringType = RegisterType.getRegisterType(RegisterType.Category.Reference, stringClassDef);
887        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, stringType);
888        return true;
889    }
890
891    private boolean handleConstClass(AnalyzedInstruction analyzedInstruction) {
892        ClassPath.ClassDef classClassDef = ClassPath.getClassDef("Ljava/lang/Class;");
893        RegisterType classType = RegisterType.getRegisterType(RegisterType.Category.Reference, classClassDef);
894
895        InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
896        Item item = instruction.getReferencedItem();
897        assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
898
899        //make sure the referenced class is resolvable
900        //TODO: need to check class access
901        ClassPath.ClassDef classDef = ClassPath.getClassDef((TypeIdItem)item);
902        return false;
903    }
904
905    private boolean handleMonitor(AnalyzedInstruction analyzedInstruction) {
906        SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction;
907
908        RegisterType registerType = analyzedInstruction.postRegisterMap[instruction.getRegisterA()];
909        assert registerType != null;
910        if (registerType.category == RegisterType.Category.Unknown) {
911            return false;
912        }
913
914        checkRegister(registerType, ReferenceCategories);
915        return true;
916    }
917
918    private boolean handleCheckCast(AnalyzedInstruction analyzedInstruction) {
919        {
920            //ensure the "source" register is a reference type
921            SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
922
923            RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA());
924            assert registerType != null;
925            if (registerType.category == RegisterType.Category.Unknown) {
926                return false;
927            }
928
929            checkRegister(registerType, ReferenceCategories);
930        }
931
932        {
933            //resolve and verify the class that we're casting to
934            InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
935
936            Item item = instruction.getReferencedItem();
937            assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
938
939            //TODO: need to check class access
940            RegisterType newDestinationRegisterType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item);
941            try {
942                checkRegister(newDestinationRegisterType, ReferenceCategories);
943            } catch (ValidationException ex) {
944                //TODO: verify that dalvik allows a non-reference type..
945                //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)
946            }
947
948            setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, newDestinationRegisterType);
949            return true;
950        }
951    }
952
953    private boolean handleInstanceOf(AnalyzedInstruction analyzedInstruction) {
954        {
955            //ensure the register that is being checks is a reference type
956            TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction;
957
958            RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
959            assert registerType != null;
960            if (registerType.category == RegisterType.Category.Unknown) {
961                return false;
962            }
963
964            checkRegister(registerType, ReferenceCategories);
965        }
966
967        {
968            //resolve and verify the class that we're checking against
969            InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
970
971            Item item = instruction.getReferencedItem();
972            assert  item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
973            RegisterType registerType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item);
974            checkRegister(registerType, ReferenceCategories);
975
976            //TODO: is it valid to use an array type?
977
978            //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.
979            setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
980                    RegisterType.getRegisterType(RegisterType.Category.Boolean, null));
981            return true;
982        }
983    }
984
985    private boolean handleArrayLength(AnalyzedInstruction analyzedInstruction) {
986        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction;
987
988        int arrayRegisterNumber = instruction.getRegisterB();
989        RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(arrayRegisterNumber);
990        assert arrayRegisterType != null;
991        if (arrayRegisterType.category == RegisterType.Category.Unknown) {
992            return false;
993        }
994
995        assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
996
997        checkRegister(arrayRegisterType, ReferenceCategories);
998        if (arrayRegisterType.type != null) {
999            if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
1000                throw new ValidationException("Cannot use array-length with non-array type " +
1001                        arrayRegisterType.type.getClassType());
1002            }
1003        }
1004
1005        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1006                RegisterType.getRegisterType(RegisterType.Category.Integer, null));
1007        return true;
1008    }
1009
1010    private boolean handleNewInstance(AnalyzedInstruction analyzedInstruction) {
1011        InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
1012
1013        Item item = instruction.getReferencedItem();
1014        assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
1015
1016        //TODO: need to check class access
1017        RegisterType classType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item);
1018        checkRegister(classType, ReferenceCategories);
1019        if (((TypeIdItem)item).getTypeDescriptor().charAt(0) == '[') {
1020            throw new ValidationException("Cannot use array type \"" + ((TypeIdItem)item).getTypeDescriptor() +
1021                    "\" with new-instance. Use new-array instead.");
1022        }
1023
1024        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1025                RegisterType.getRegisterType(RegisterType.Category.UninitRef, classType.type));
1026        return true;
1027    }
1028
1029    private boolean handleNewArray(AnalyzedInstruction analyzedInstruction) {
1030        {
1031            TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
1032
1033            int sizeRegister = instruction.getRegisterB();
1034            RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(sizeRegister);
1035            assert registerType != null;
1036
1037            if (registerType.category == RegisterType.Category.Unknown) {
1038                return false;
1039            }
1040
1041            checkRegister(registerType, Primitive32BitCategories);
1042        }
1043
1044        InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
1045
1046        Item item = instruction.getReferencedItem();
1047        assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
1048
1049        RegisterType arrayType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item);
1050        assert arrayType.type instanceof ClassPath.ArrayClassDef;
1051
1052        checkRegister(arrayType, ReferenceCategories);
1053        if (arrayType.type.getClassType().charAt(0) != '[') {
1054            throw new ValidationException("Cannot use non-array type \"" + arrayType.type.getClassType() +
1055                    "\" with new-array. Use new-instance instead.");
1056        }
1057
1058        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, arrayType);
1059        return true;
1060    }
1061
1062    private static interface RegisterIterator {
1063        int getRegister();
1064        boolean moveNext();
1065    }
1066
1067    private boolean handleFilledNewArrayCommon(AnalyzedInstruction analyzedInstruction,
1068                                               RegisterIterator registerIterator) {
1069        InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
1070
1071        RegisterType arrayType;
1072        RegisterType arrayImmediateElementType;
1073
1074        Item item = instruction.getReferencedItem();
1075        assert  item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
1076
1077        ClassPath.ClassDef classDef = ClassPath.getClassDef((TypeIdItem)item);
1078
1079        if (classDef.getClassType().charAt(0) != '[') {
1080            throw new ValidationException("Cannot use non-array type \"" + classDef.getClassType() +
1081                "\" with new-array. Use new-instance instead.");
1082        }
1083
1084        ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)classDef;
1085        arrayType = RegisterType.getRegisterType(RegisterType.Category.Reference, classDef);
1086        arrayImmediateElementType = RegisterType.getRegisterTypeForType(
1087                arrayClassDef.getImmediateElementClass().getClassType());
1088        String baseElementType = arrayClassDef.getBaseElementClass().getClassType();
1089        if (baseElementType.charAt(0) == 'J' || baseElementType.charAt(0) == 'D') {
1090            throw new ValidationException("Cannot use filled-new-array to create an array of wide values " +
1091                    "(long or double)");
1092        }
1093
1094        do {
1095            int register = registerIterator.getRegister();
1096            RegisterType elementType = analyzedInstruction.getPreInstructionRegisterType(register);
1097            assert elementType != null;
1098
1099            if (elementType.category == RegisterType.Category.Unknown) {
1100                return false;
1101            }
1102
1103            if (!elementType.canBeAssignedTo(arrayImmediateElementType)) {
1104                throw new ValidationException("Register v" + Integer.toString(register) + " is of type " +
1105                        elementType.toString() + " and is incompatible with the array type " +
1106                        arrayType.type.getClassType());
1107            }
1108        } while (registerIterator.moveNext());
1109
1110        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, arrayType);
1111        return true;
1112    }
1113
1114    private boolean handleFilledNewArray(AnalyzedInstruction analyzedInstruction) {
1115        FiveRegisterInstruction instruction = (FiveRegisterInstruction)analyzedInstruction.instruction;
1116        final int registerCount = instruction.getRegCount();
1117        final int[] registers = new int[]{instruction.getRegisterD(), instruction.getRegisterE(),
1118                                          instruction.getRegisterF(), instruction.getRegisterG(),
1119                                          instruction.getRegisterA()};
1120
1121        return handleFilledNewArrayCommon(analyzedInstruction,
1122                new RegisterIterator() {
1123                    private int currentRegister = 0;
1124                    public int getRegister() {
1125                        return registers[currentRegister];
1126                    }
1127
1128                    public boolean moveNext() {
1129                        currentRegister++;
1130                        if (currentRegister >= registerCount) {
1131                            return false;
1132                        }
1133                        return true;
1134                    }
1135                });
1136    }
1137
1138    private boolean handleFilledNewArrayRange(AnalyzedInstruction analyzedInstruction) {
1139        final RegisterRangeInstruction instruction = (RegisterRangeInstruction)analyzedInstruction.instruction;
1140
1141        //instruction.getStartRegister() and instruction.getRegCount() both return an int value, but are actually
1142        //unsigned 16 bit values, so we don't have to worry about overflowing an int when adding them together
1143        if (instruction.getStartRegister() + instruction.getRegCount() >= 1<<16) {
1144            throw new ValidationException(String.format("Invalid register range {v%d .. v%d}. The ending register " +
1145                    "is larger than the largest allowed register of v65535.",
1146                    instruction.getStartRegister(),
1147                    instruction.getStartRegister() + instruction.getRegCount() - 1));
1148        }
1149
1150        return handleFilledNewArrayCommon(analyzedInstruction,
1151                new RegisterIterator() {
1152                    private int currentRegister = 0;
1153                    private final int startRegister = instruction.getStartRegister();
1154                    private final int registerCount = instruction.getRegCount();
1155
1156                    public int getRegister() {
1157                        return startRegister + currentRegister;
1158                    }
1159
1160                    public boolean moveNext() {
1161                        currentRegister++;
1162                        if (currentRegister >= registerCount) {
1163                            return false;
1164                        }
1165                        return true;
1166                    }
1167                });
1168    }
1169
1170    private boolean handleFillArrayData(AnalyzedInstruction analyzedInstruction) {
1171        SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
1172
1173        int register = instruction.getRegisterA();
1174        RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(register);
1175        assert registerType != null;
1176
1177        if (registerType.category == RegisterType.Category.Unknown ||
1178            registerType.category == RegisterType.Category.Null) {
1179            return false;
1180        }
1181
1182        if (registerType.category != RegisterType.Category.Reference) {
1183            throw new ValidationException(String.format("Cannot use fill-array-data with non-array register v%d of " +
1184                    "type %s", register, registerType.toString()));
1185        }
1186
1187        assert registerType.type instanceof ClassPath.ArrayClassDef;
1188        ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)registerType.type;
1189
1190        if (arrayClassDef.getArrayDimensions() != 1) {
1191            throw new ValidationException(String.format("Cannot use fill-array-data with array type %s. It can only " +
1192                    "be used with a one-dimensional array of primitives.", arrayClassDef.getClassType()));
1193        }
1194
1195        int elementWidth;
1196        switch (arrayClassDef.getBaseElementClass().getClassType().charAt(0)) {
1197            case 'Z':
1198            case 'B':
1199                elementWidth = 1;
1200                break;
1201            case 'C':
1202            case 'S':
1203                elementWidth = 2;
1204                break;
1205            case 'I':
1206            case 'F':
1207                elementWidth = 4;
1208                break;
1209            case 'J':
1210            case 'D':
1211                elementWidth = 8;
1212                break;
1213            default:
1214                throw new ValidationException(String.format("Cannot use fill-array-data with array type %s. It can " +
1215                        "only be used with a one-dimensional array of primitives.", arrayClassDef.getClassType()));
1216        }
1217
1218
1219        int arrayDataAddressOffset = ((OffsetInstruction)analyzedInstruction.instruction).getTargetAddressOffset();
1220        int arrayDataCodeAddress = getInstructionAddress(analyzedInstruction) + arrayDataAddressOffset;
1221        AnalyzedInstruction arrayDataInstruction = this.instructions.get(arrayDataCodeAddress);
1222        if (arrayDataInstruction == null || arrayDataInstruction.instruction.getFormat() != Format.ArrayData) {
1223            throw new ValidationException(String.format("Could not find an array data structure at code address 0x%x",
1224                    arrayDataCodeAddress));
1225        }
1226
1227        ArrayDataPseudoInstruction arrayDataPseudoInstruction =
1228                (ArrayDataPseudoInstruction)arrayDataInstruction.instruction;
1229
1230        if (elementWidth != arrayDataPseudoInstruction.getElementWidth()) {
1231            throw new ValidationException(String.format("The array data at code address 0x%x does not have the " +
1232                    "correct element width for array type %s. Expecting element width %d, got element width %d.",
1233                    arrayDataCodeAddress, arrayClassDef.getClassType(), elementWidth,
1234                    arrayDataPseudoInstruction.getElementWidth()));
1235        }
1236
1237        return true;
1238    }
1239
1240    private boolean handleThrow(AnalyzedInstruction analyzedInstruction) {
1241        int register = ((SingleRegisterInstruction)analyzedInstruction.instruction).getRegisterA();
1242
1243        RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(register);
1244        assert registerType != null;
1245
1246        if (registerType.category == RegisterType.Category.Unknown) {
1247            return false;
1248        }
1249
1250        if (registerType.category == RegisterType.Category.Null) {
1251            return true;
1252        }
1253
1254        if (registerType.category != RegisterType.Category.Reference) {
1255            throw new ValidationException(String.format("Cannot use throw with non-reference type %s in register v%d",
1256                    registerType.toString(), register));
1257        }
1258
1259        assert registerType.type != null;
1260
1261        if (!registerType.type.extendsClass(ClassPath.getClassDef("Ljava/lang/Throwable;"))) {
1262            throw new ValidationException(String.format("Cannot use throw with non-throwable type %s in register v%d",
1263                    registerType.type.getClassType(), register));
1264        }
1265
1266        return true;
1267    }
1268
1269    private boolean handleSwitch(AnalyzedInstruction analyzedInstruction, Format expectedSwitchDataFormat) {
1270        int register = ((SingleRegisterInstruction)analyzedInstruction.instruction).getRegisterA();
1271        int switchCodeAddressOffset = ((OffsetInstruction)analyzedInstruction.instruction).getTargetAddressOffset();
1272
1273        RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(register);
1274        assert registerType != null;
1275
1276        if (registerType.category == RegisterType.Category.Unknown) {
1277            return false;
1278        }
1279
1280        checkRegister(registerType, Primitive32BitCategories);
1281
1282        int switchDataCodeAddress = this.getInstructionAddress(analyzedInstruction) + switchCodeAddressOffset;
1283        AnalyzedInstruction switchDataAnalyzedInstruction = instructions.get(switchDataCodeAddress);
1284
1285        if (switchDataAnalyzedInstruction == null ||
1286            switchDataAnalyzedInstruction.instruction.getFormat() != expectedSwitchDataFormat) {
1287            throw new ValidationException(String.format("There is no %s structure at code address 0x%x",
1288                    expectedSwitchDataFormat.name(), switchDataCodeAddress));
1289        }
1290
1291        return true;
1292    }
1293
1294    private boolean handleFloatCmp(AnalyzedInstruction analyzedInstruction) {
1295        ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
1296
1297        RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1298        assert registerType != null;
1299
1300        if (registerType.category == RegisterType.Category.Unknown) {
1301            return false;
1302        }
1303        checkRegister(registerType, Primitive32BitCategories);
1304
1305        registerType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterC());
1306        assert registerType != null;
1307
1308        if (registerType.category == RegisterType.Category.Unknown) {
1309            return false;
1310        }
1311        checkRegister(registerType, Primitive32BitCategories);
1312
1313        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1314                RegisterType.getRegisterType(RegisterType.Category.Byte, null));
1315        return true;
1316    }
1317
1318    private boolean handleWideCmp(AnalyzedInstruction analyzedInstruction) {
1319        ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
1320
1321        RegisterType registerType = getAndCheckWideSourcePair(analyzedInstruction, instruction.getRegisterB());
1322        assert registerType != null;
1323        if (registerType.category == RegisterType.Category.Unknown) {
1324            return false;
1325        }
1326
1327        registerType = getAndCheckWideSourcePair(analyzedInstruction, instruction.getRegisterC());
1328        assert registerType != null;
1329        if (registerType.category == RegisterType.Category.Unknown) {
1330            return false;
1331        }
1332
1333        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1334                RegisterType.getRegisterType(RegisterType.Category.Byte, null));
1335        return true;
1336    }
1337
1338    private boolean handleIfEqNe(AnalyzedInstruction analyzedInstruction) {
1339        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
1340
1341        RegisterType registerType1 = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA());
1342        assert registerType1 != null;
1343        if (registerType1.category == RegisterType.Category.Unknown) {
1344            return false;
1345        }
1346
1347        RegisterType registerType2 = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1348        assert registerType2 != null;
1349        if (registerType2.category == RegisterType.Category.Unknown) {
1350            return false;
1351        }
1352
1353        if (!(
1354                (ReferenceCategories.contains(registerType1.category) &&
1355                ReferenceCategories.contains(registerType2.category))
1356                    ||
1357                (Primitive32BitCategories.contains(registerType1.category) &&
1358                Primitive32BitCategories.contains(registerType2.category))
1359              )) {
1360
1361            throw new ValidationException(String.format("%s cannot be used on registers of dissimilar types %s and " +
1362                    "%s. They must both be a reference type or a primitive 32 bit type.",
1363                    analyzedInstruction.instruction.opcode.name, registerType1.toString(), registerType2.toString()));
1364        }
1365
1366        return true;
1367    }
1368
1369    private boolean handleIf(AnalyzedInstruction analyzedInstruction) {
1370        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
1371
1372        RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA());
1373        assert registerType != null;
1374
1375        if (registerType.category == RegisterType.Category.Unknown) {
1376            return false;
1377        }
1378        checkRegister(registerType, Primitive32BitCategories);
1379
1380        registerType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1381        assert registerType != null;
1382
1383        if (registerType.category == RegisterType.Category.Unknown) {
1384            return false;
1385        }
1386        checkRegister(registerType, Primitive32BitCategories);
1387
1388        return true;
1389    }
1390
1391    private boolean handleIfEqzNez(AnalyzedInstruction analyzedInstruction) {
1392        SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
1393
1394        RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA());
1395        assert registerType != null;
1396        if (registerType.category == RegisterType.Category.Unknown) {
1397            return false;
1398        }
1399
1400        if (!ReferenceCategories.contains(registerType.category) &&
1401            !Primitive32BitCategories.contains(registerType.category)) {
1402            throw new ValidationException(String.format("%s cannot be used with register type %s. Expecting 32-bit " +
1403                    "primitive type or reference type.", analyzedInstruction.instruction.opcode));
1404        }
1405
1406        return true;
1407    }
1408
1409    private boolean handleIfz(AnalyzedInstruction analyzedInstruction) {
1410        SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
1411
1412        RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA());
1413        assert registerType != null;
1414
1415        if (registerType.category == RegisterType.Category.Unknown) {
1416            return false;
1417        }
1418        checkRegister(registerType, Primitive32BitCategories);
1419
1420        return true;
1421    }
1422
1423    private boolean handle32BitPrimitiveAget(AnalyzedInstruction analyzedInstruction,
1424                                             RegisterType.Category instructionCategory) {
1425        ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
1426
1427        RegisterType indexRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterC());
1428        assert indexRegisterType != null;
1429        if (indexRegisterType.category == RegisterType.Category.Unknown) {
1430            return false;
1431        }
1432        checkRegister(indexRegisterType, Primitive32BitCategories);
1433
1434        RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1435        assert arrayRegisterType != null;
1436        if (indexRegisterType.category == RegisterType.Category.Unknown) {
1437            return false;
1438        }
1439
1440        if (arrayRegisterType.category != RegisterType.Category.Null) {
1441            if (arrayRegisterType.category != RegisterType.Category.Reference) {
1442                throw new ValidationException(String.format("Cannot use %s with non-array type %s",
1443                        analyzedInstruction.instruction.opcode.name, arrayRegisterType.category.toString()));
1444            }
1445
1446            assert arrayRegisterType.type != null;
1447            if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
1448                throw new ValidationException(String.format("Cannot use %s with non-array type %s",
1449                        analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType()));
1450            }
1451
1452            assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
1453            ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type;
1454
1455            if (arrayClassDef.getArrayDimensions() != 1) {
1456                throw new ValidationException(String.format("Cannot use %s with multi-dimensional array type %s",
1457                        analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType()));
1458            }
1459
1460            RegisterType arrayBaseType =
1461                    RegisterType.getRegisterTypeForType(arrayClassDef.getBaseElementClass().getClassType());
1462            if (checkArrayFieldAssignment(arrayBaseType.category, instructionCategory)) {
1463                throw new ValidationException(String.format("Cannot use %s with array type %s. Incorrect array type " +
1464                        "for the instruction.", analyzedInstruction.instruction.opcode.name,
1465                        arrayRegisterType.type.getClassType()));
1466            }
1467        }
1468
1469        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1470                RegisterType.getRegisterType(instructionCategory, null));
1471
1472        return true;
1473    }
1474
1475    private boolean handleAgetWide(AnalyzedInstruction analyzedInstruction) {
1476        ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
1477
1478        RegisterType indexRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterC());
1479        assert indexRegisterType != null;
1480        if (indexRegisterType.category == RegisterType.Category.Unknown) {
1481            return false;
1482        }
1483        checkRegister(indexRegisterType, Primitive32BitCategories);
1484
1485        RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1486        assert arrayRegisterType != null;
1487        if (indexRegisterType.category == RegisterType.Category.Unknown) {
1488            return false;
1489        }
1490
1491        if (arrayRegisterType.category != RegisterType.Category.Null) {
1492            if (arrayRegisterType.category != RegisterType.Category.Reference) {
1493                throw new ValidationException(String.format("Cannot use aget-wide with non-array type %s",
1494                        arrayRegisterType.category.toString()));
1495            }
1496
1497            assert arrayRegisterType.type != null;
1498            if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
1499                throw new ValidationException(String.format("Cannot use aget-wide with non-array type %s",
1500                        arrayRegisterType.type.getClassType()));
1501            }
1502
1503            assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
1504            ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type;
1505
1506            if (arrayClassDef.getArrayDimensions() != 1) {
1507                throw new ValidationException(String.format("Cannot use aget-wide with multi-dimensional array type %s",
1508                        arrayRegisterType.type.getClassType()));
1509            }
1510
1511            char arrayBaseType = arrayClassDef.getBaseElementClass().getClassType().charAt(0);
1512            if (arrayBaseType == 'J') {
1513                setWideDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1514                        RegisterType.getRegisterType(RegisterType.Category.LongLo, null));
1515            } else if (arrayBaseType == 'D') {
1516                setWideDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1517                        RegisterType.getRegisterType(RegisterType.Category.DoubleLo, null));
1518            } else {
1519                throw new ValidationException(String.format("Cannot use aget-wide with array type %s. Incorrect " +
1520                        "array type for the instruction.", arrayRegisterType.type.getClassType()));
1521            }
1522        } else {
1523            setWideDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1524                        RegisterType.getRegisterType(RegisterType.Category.LongLo, null));
1525        }
1526
1527        return true;
1528    }
1529
1530    private boolean handleAgetObject(AnalyzedInstruction analyzedInstruction) {
1531        ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
1532
1533        RegisterType indexRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterC());
1534        assert indexRegisterType != null;
1535        if (indexRegisterType.category == RegisterType.Category.Unknown) {
1536            return false;
1537        }
1538        checkRegister(indexRegisterType, Primitive32BitCategories);
1539
1540        RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1541        assert arrayRegisterType != null;
1542        if (indexRegisterType.category == RegisterType.Category.Unknown) {
1543            return false;
1544        }
1545
1546        if (arrayRegisterType.category != RegisterType.Category.Null) {
1547            if (arrayRegisterType.category != RegisterType.Category.Reference) {
1548                throw new ValidationException(String.format("Cannot use aget-object with non-array type %s",
1549                        arrayRegisterType.category.toString()));
1550            }
1551
1552            assert arrayRegisterType.type != null;
1553            if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
1554                throw new ValidationException(String.format("Cannot use aget-object with non-array type %s",
1555                        arrayRegisterType.type.getClassType()));
1556            }
1557
1558            assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
1559            ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type;
1560
1561            ClassPath.ClassDef elementClassDef = arrayClassDef.getImmediateElementClass();
1562            char elementTypePrefix = elementClassDef.getClassType().charAt(0);
1563            if (elementTypePrefix != 'L' && elementTypePrefix != '[') {
1564                throw new ValidationException(String.format("Cannot use aget-object with array type %s. Incorrect " +
1565                        "array type for the instruction.", arrayRegisterType.type.getClassType()));
1566            }
1567
1568            setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1569                    RegisterType.getRegisterType(RegisterType.Category.Reference, elementClassDef));
1570        } else {
1571            setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1572                    RegisterType.getRegisterType(RegisterType.Category.Null, null));
1573        }
1574
1575        return true;
1576    }
1577
1578    private boolean handle32BitPrimitiveAput(AnalyzedInstruction analyzedInstruction,
1579                                             RegisterType.Category instructionCategory) {
1580        ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
1581
1582        RegisterType indexRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterC());
1583        assert indexRegisterType != null;
1584        if (indexRegisterType.category == RegisterType.Category.Unknown) {
1585            return false;
1586        }
1587        checkRegister(indexRegisterType, Primitive32BitCategories);
1588
1589
1590        RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA());
1591        assert sourceRegisterType != null;
1592        if (sourceRegisterType.category == RegisterType.Category.Unknown) {
1593            return false;
1594        }
1595        RegisterType instructionRegisterType = RegisterType.getRegisterType(instructionCategory, null);
1596        if (!sourceRegisterType.canBeAssignedTo(instructionRegisterType)) {
1597            throw new ValidationException(String.format("Cannot use %s with source register type %s.",
1598                    analyzedInstruction.instruction.opcode.name, sourceRegisterType.toString()));
1599        }
1600
1601
1602        RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1603        assert arrayRegisterType != null;
1604        if (indexRegisterType.category == RegisterType.Category.Unknown) {
1605            return false;
1606        }
1607
1608        if (arrayRegisterType.category != RegisterType.Category.Null) {
1609            if (arrayRegisterType.category != RegisterType.Category.Reference) {
1610                throw new ValidationException(String.format("Cannot use %s with non-array type %s",
1611                        analyzedInstruction.instruction.opcode.name, arrayRegisterType.category.toString()));
1612            }
1613
1614            assert arrayRegisterType.type != null;
1615            if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
1616                throw new ValidationException(String.format("Cannot use %s with non-array type %s",
1617                        analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType()));
1618            }
1619
1620            assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
1621            ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type;
1622
1623            if (arrayClassDef.getArrayDimensions() != 1) {
1624                throw new ValidationException(String.format("Cannot use %s with multi-dimensional array type %s",
1625                        analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType()));
1626            }
1627
1628            RegisterType arrayBaseType =
1629                    RegisterType.getRegisterTypeForType(arrayClassDef.getBaseElementClass().getClassType());
1630            if (checkArrayFieldAssignment(arrayBaseType.category, instructionCategory)) {
1631                throw new ValidationException(String.format("Cannot use %s with array type %s. Incorrect array type " +
1632                        "for the instruction.", analyzedInstruction.instruction.opcode.name,
1633                        arrayRegisterType.type.getClassType()));
1634            }
1635        }
1636
1637        return true;
1638    }
1639
1640    private boolean handleAputWide(AnalyzedInstruction analyzedInstruction) {
1641        ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
1642
1643        RegisterType indexRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterC());
1644        assert indexRegisterType != null;
1645        if (indexRegisterType.category == RegisterType.Category.Unknown) {
1646            return false;
1647        }
1648        checkRegister(indexRegisterType, Primitive32BitCategories);
1649
1650        RegisterType sourceRegisterType = getAndCheckWideSourcePair(analyzedInstruction, instruction.getRegisterA());
1651        if (sourceRegisterType.category == RegisterType.Category.Unknown) {
1652            return false;
1653        }
1654
1655        RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1656        assert arrayRegisterType != null;
1657        if (indexRegisterType.category == RegisterType.Category.Unknown) {
1658            return false;
1659        }
1660
1661        if (arrayRegisterType.category != RegisterType.Category.Null) {
1662            if (arrayRegisterType.category != RegisterType.Category.Reference) {
1663                throw new ValidationException(String.format("Cannot use aput-wide with non-array type %s",
1664                        arrayRegisterType.category.toString()));
1665            }
1666
1667            assert arrayRegisterType.type != null;
1668            if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
1669                throw new ValidationException(String.format("Cannot use aput-wide with non-array type %s",
1670                        arrayRegisterType.type.getClassType()));
1671            }
1672
1673            assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
1674            ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type;
1675
1676            if (arrayClassDef.getArrayDimensions() != 1) {
1677                throw new ValidationException(String.format("Cannot use aput-wide with multi-dimensional array type %s",
1678                        arrayRegisterType.type.getClassType()));
1679            }
1680
1681            char arrayBaseType = arrayClassDef.getBaseElementClass().getClassType().charAt(0);
1682            if (arrayBaseType != 'J' && arrayBaseType != 'D') {
1683                throw new ValidationException(String.format("Cannot use aput-wide with array type %s. Incorrect " +
1684                        "array type for the instruction.", arrayRegisterType.type.getClassType()));
1685            }
1686        }
1687
1688        return true;
1689    }
1690
1691    private boolean handleAputObject(AnalyzedInstruction analyzedInstruction) {
1692        ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
1693
1694        RegisterType indexRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterC());
1695        assert indexRegisterType != null;
1696        if (indexRegisterType.category == RegisterType.Category.Unknown) {
1697            return false;
1698        }
1699        checkRegister(indexRegisterType, Primitive32BitCategories);
1700
1701        RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA());
1702        assert sourceRegisterType != null;
1703        if (sourceRegisterType.category == RegisterType.Category.Unknown) {
1704            return false;
1705        }
1706
1707        RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1708        assert arrayRegisterType != null;
1709        if (indexRegisterType.category == RegisterType.Category.Unknown) {
1710            return false;
1711        }
1712
1713        if (arrayRegisterType.category != RegisterType.Category.Null) {
1714            //don't check the source type against the array type, just make sure it is an array of reference types
1715
1716            if (arrayRegisterType.category != RegisterType.Category.Reference) {
1717                throw new ValidationException(String.format("Cannot use aget-object with non-array type %s",
1718                        arrayRegisterType.category.toString()));
1719            }
1720
1721            assert arrayRegisterType.type != null;
1722            if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
1723                throw new ValidationException(String.format("Cannot use aget-object with non-array type %s",
1724                        arrayRegisterType.type.getClassType()));
1725            }
1726
1727            assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
1728            ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type;
1729
1730            ClassPath.ClassDef elementClassDef = arrayClassDef.getImmediateElementClass();
1731            char elementTypePrefix = elementClassDef.getClassType().charAt(0);
1732            if (elementTypePrefix != 'L' && elementTypePrefix != '[') {
1733                throw new ValidationException(String.format("Cannot use aget-object with array type %s. Incorrect " +
1734                        "array type for the instruction.", arrayRegisterType.type.getClassType()));
1735            }
1736        }
1737
1738        return true;
1739    }
1740
1741    private boolean handle32BitPrimitiveIget(AnalyzedInstruction analyzedInstruction,
1742                                             RegisterType.Category instructionCategory) {
1743        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
1744
1745        RegisterType objectRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1746        assert objectRegisterType != null;
1747        if (objectRegisterType.category == RegisterType.Category.Unknown) {
1748            return false;
1749        }
1750        checkRegister(objectRegisterType, ReferenceCategories);
1751
1752        //TODO: check access
1753        //TODO: allow an uninitialized "this" reference, if the current method is an <init> method
1754        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
1755        assert referencedItem instanceof FieldIdItem;
1756        FieldIdItem field = (FieldIdItem)referencedItem;
1757
1758        if (objectRegisterType.category != RegisterType.Category.Null &&
1759            !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) {
1760            throw new ValidationException(String.format("Cannot access field %s through type %s",
1761                    field.getFieldString(), objectRegisterType.type.getClassType()));
1762        }
1763
1764        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
1765
1766        if (!checkArrayFieldAssignment(fieldType.category, instructionCategory)) {
1767                throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
1768                        "for the instruction.", analyzedInstruction.instruction.opcode.name,
1769                        field.getFieldString()));
1770        }
1771
1772        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1773                RegisterType.getRegisterType(instructionCategory, null));
1774
1775        return true;
1776    }
1777
1778    private boolean handleIgetWide(AnalyzedInstruction analyzedInstruction) {
1779        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
1780
1781        RegisterType objectRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1782        assert objectRegisterType != null;
1783        if (objectRegisterType.category == RegisterType.Category.Unknown) {
1784            return false;
1785        }
1786        checkRegister(objectRegisterType, ReferenceCategories);
1787
1788        getAndCheckWideSourcePair(analyzedInstruction, instruction.getRegisterB());
1789
1790        //TODO: check access
1791        //TODO: allow an uninitialized "this" reference, if the current method is an <init> method
1792        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
1793        assert referencedItem instanceof FieldIdItem;
1794        FieldIdItem field = (FieldIdItem)referencedItem;
1795
1796        if (objectRegisterType.category != RegisterType.Category.Null &&
1797            !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) {
1798            throw new ValidationException(String.format("Cannot access field %s through type %s",
1799                    field.getFieldString(), objectRegisterType.type.getClassType()));
1800        }
1801
1802        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
1803
1804        try {
1805            checkRegister(fieldType, WideLowCategories);
1806        } catch (ValidationException ex) {
1807            throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
1808                        "for the instruction.", analyzedInstruction.instruction.opcode.name,
1809                        field.getFieldString()));
1810        }
1811
1812        setWideDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, fieldType);
1813
1814        return true;
1815    }
1816
1817    private boolean handleIgetObject(AnalyzedInstruction analyzedInstruction) {
1818        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
1819
1820        RegisterType objectRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1821        assert objectRegisterType != null;
1822        if (objectRegisterType.category == RegisterType.Category.Unknown) {
1823            return false;
1824        }
1825        checkRegister(objectRegisterType, ReferenceCategories);
1826
1827        //TODO: check access
1828        //TODO: allow an uninitialized "this" reference, if the current method is an <init> method
1829        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
1830        assert referencedItem instanceof FieldIdItem;
1831        FieldIdItem field = (FieldIdItem)referencedItem;
1832
1833        if (objectRegisterType.category != RegisterType.Category.Null &&
1834            !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) {
1835            throw new ValidationException(String.format("Cannot access field %s through type %s",
1836                    field.getFieldString(), objectRegisterType.type.getClassType()));
1837        }
1838
1839        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
1840
1841        if (fieldType.category != RegisterType.Category.Reference) {
1842            throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
1843                        "for the instruction.", analyzedInstruction.instruction.opcode.name,
1844                        field.getFieldString()));
1845        }
1846
1847        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, fieldType);
1848
1849        return true;
1850    }
1851
1852    private boolean handle32BitPrimitiveIput(AnalyzedInstruction analyzedInstruction,
1853                                             RegisterType.Category instructionCategory) {
1854        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
1855
1856        RegisterType objectRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1857        assert objectRegisterType != null;
1858        if (objectRegisterType.category == RegisterType.Category.Unknown) {
1859            return false;
1860        }
1861        checkRegister(objectRegisterType, ReferenceCategories);
1862
1863        RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA());
1864        assert sourceRegisterType != null;
1865        if (sourceRegisterType.category == RegisterType.Category.Unknown) {
1866            return false;
1867        }
1868
1869        //per CodeVerify.c in dalvik:
1870        //java generates synthetic functions that write byte values into boolean fields
1871        if (sourceRegisterType.category == RegisterType.Category.Byte &&
1872            instructionCategory == RegisterType.Category.Boolean) {
1873
1874            sourceRegisterType = RegisterType.getRegisterType(RegisterType.Category.Boolean, null);
1875        }
1876
1877        RegisterType instructionRegisterType = RegisterType.getRegisterType(instructionCategory, null);
1878        if (!sourceRegisterType.canBeAssignedTo(instructionRegisterType)) {
1879            throw new ValidationException(String.format("Cannot use %s with source register type %s.",
1880                    analyzedInstruction.instruction.opcode.name, sourceRegisterType.toString()));
1881        }
1882
1883
1884        //TODO: check access
1885        //TODO: allow an uninitialized "this" reference, if the current method is an <init> method
1886        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
1887        assert referencedItem instanceof FieldIdItem;
1888        FieldIdItem field = (FieldIdItem)referencedItem;
1889
1890        if (objectRegisterType.category != RegisterType.Category.Null &&
1891            !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) {
1892            throw new ValidationException(String.format("Cannot access field %s through type %s",
1893                    field.getFieldString(), objectRegisterType.type.getClassType()));
1894        }
1895
1896        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
1897
1898        if (!checkArrayFieldAssignment(fieldType.category, instructionCategory)) {
1899                throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
1900                        "for the instruction.", analyzedInstruction.instruction.opcode.name,
1901                        field.getFieldString()));
1902        }
1903
1904        return true;
1905    }
1906
1907    private boolean handleIputWide(AnalyzedInstruction analyzedInstruction) {
1908        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
1909
1910        RegisterType objectRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1911        assert objectRegisterType != null;
1912        if (objectRegisterType.category == RegisterType.Category.Unknown) {
1913            return false;
1914        }
1915        checkRegister(objectRegisterType, ReferenceCategories);
1916
1917        RegisterType sourceRegisterType = getAndCheckWideSourcePair(analyzedInstruction, instruction.getRegisterA());
1918        assert sourceRegisterType != null;
1919        if (sourceRegisterType.category == RegisterType.Category.Unknown) {
1920            return false;
1921        }
1922
1923        //TODO: check access
1924        //TODO: allow an uninitialized "this" reference, if the current method is an <init> method
1925        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
1926        assert referencedItem instanceof FieldIdItem;
1927        FieldIdItem field = (FieldIdItem)referencedItem;
1928
1929        if (objectRegisterType.category != RegisterType.Category.Null &&
1930                !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) {
1931            throw new ValidationException(String.format("Cannot access field %s through type %s",
1932                    field.getFieldString(), objectRegisterType.type.getClassType()));
1933        }
1934
1935        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
1936
1937        if (!WideLowCategories.contains(fieldType.category)) {
1938            throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
1939                    "for the instruction.", analyzedInstruction.instruction.opcode.name,
1940                    field.getFieldString()));
1941        }
1942
1943        return true;
1944    }
1945
1946    private boolean handleIputObject(AnalyzedInstruction analyzedInstruction) {
1947        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
1948
1949        RegisterType objectRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1950        assert objectRegisterType != null;
1951        if (objectRegisterType.category == RegisterType.Category.Unknown) {
1952            return false;
1953        }
1954        checkRegister(objectRegisterType, ReferenceCategories);
1955
1956        RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA());
1957        assert sourceRegisterType != null;
1958        if (sourceRegisterType.category == RegisterType.Category.Unknown) {
1959            return false;
1960        }
1961        checkRegister(objectRegisterType, ReferenceCategories);
1962
1963        //TODO: check access
1964        //TODO: allow an uninitialized "this" reference, if the current method is an <init> method
1965        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
1966        assert referencedItem instanceof FieldIdItem;
1967        FieldIdItem field = (FieldIdItem)referencedItem;
1968
1969        if (objectRegisterType.category != RegisterType.Category.Null &&
1970            !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) {
1971            throw new ValidationException(String.format("Cannot access field %s through type %s",
1972                    field.getFieldString(), objectRegisterType.type.getClassType()));
1973        }
1974
1975        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
1976
1977        if (fieldType.category != RegisterType.Category.Reference) {
1978            throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
1979                        "for the instruction.", analyzedInstruction.instruction.opcode.name,
1980                        field.getFieldString()));
1981        }
1982
1983        if (sourceRegisterType.category != RegisterType.Category.Null &&
1984            !fieldType.type.isInterface() &&
1985            !sourceRegisterType.type.extendsClass(fieldType.type)) {
1986
1987            throw new ValidationException(String.format("Cannot store a value of type %s into a field of type %s",
1988                    sourceRegisterType.type.getClassType(), fieldType.type.getClassType()));
1989        }
1990
1991        return true;
1992    }
1993
1994    private boolean handle32BitPrimitiveSget(AnalyzedInstruction analyzedInstruction,
1995                                             RegisterType.Category instructionCategory) {
1996        //TODO: check access
1997        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
1998        assert referencedItem instanceof FieldIdItem;
1999        FieldIdItem field = (FieldIdItem)referencedItem;
2000
2001        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
2002
2003        if (!checkArrayFieldAssignment(fieldType.category, instructionCategory)) {
2004                throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
2005                        "for the instruction.", analyzedInstruction.instruction.opcode.name,
2006                        field.getFieldString()));
2007        }
2008
2009        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
2010                RegisterType.getRegisterType(instructionCategory, null));
2011
2012        return true;
2013    }
2014
2015    private boolean handleSgetWide(AnalyzedInstruction analyzedInstruction) {
2016        //TODO: check access
2017        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
2018        assert referencedItem instanceof FieldIdItem;
2019        FieldIdItem field = (FieldIdItem)referencedItem;
2020
2021        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
2022
2023
2024        if (fieldType.category != RegisterType.Category.LongLo &&
2025            fieldType.category != RegisterType.Category.DoubleLo) {
2026
2027            throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
2028                    "for the instruction.", analyzedInstruction.instruction.opcode.name,
2029                    field.getFieldString()));
2030        }
2031
2032        setWideDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, fieldType);
2033
2034        return true;
2035    }
2036
2037    private boolean handleSgetObject(AnalyzedInstruction analyzedInstruction) {
2038        //TODO: check access
2039        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
2040        assert referencedItem instanceof FieldIdItem;
2041        FieldIdItem field = (FieldIdItem)referencedItem;
2042
2043        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
2044
2045        if (fieldType.category != RegisterType.Category.Reference) {
2046                throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
2047                        "for the instruction.", analyzedInstruction.instruction.opcode.name,
2048                        field.getFieldString()));
2049        }
2050
2051        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, fieldType);
2052
2053        return true;
2054    }
2055
2056    private static boolean checkArrayFieldAssignment(RegisterType.Category arrayFieldCategory,
2057                                                  RegisterType.Category instructionCategory) {
2058        if (arrayFieldCategory == instructionCategory) {
2059            return true;
2060        }
2061
2062        if ((arrayFieldCategory == RegisterType.Category.Integer &&
2063             instructionCategory == RegisterType.Category.Float) ||
2064            (arrayFieldCategory == RegisterType.Category.Float &&
2065             instructionCategory == RegisterType.Category.Integer)) {
2066            return true;
2067        }
2068        return false;
2069    }
2070
2071    private static void checkRegister(RegisterType registerType, EnumSet validCategories) {
2072        if (!validCategories.contains(registerType.category)) {
2073            //TODO: add expected categories to error message
2074            throw new ValidationException("Invalid register type. Expecting one of: " + " but got \"" +
2075                    registerType.category + "\"");
2076        }
2077    }
2078
2079    private static void checkWideDestinationPair(AnalyzedInstruction analyzedInstruction) {
2080        int register = analyzedInstruction.getDestinationRegister();
2081
2082        if (register == (analyzedInstruction.postRegisterMap.length - 1)) {
2083            throw new ValidationException("v" + register + " is the last register and not a valid wide register " +
2084                    "pair.");
2085        }
2086    }
2087
2088    private static RegisterType getAndCheckWideSourcePair(AnalyzedInstruction analyzedInstruction, int firstRegister) {
2089        assert firstRegister >= 0 && firstRegister < analyzedInstruction.postRegisterMap.length;
2090
2091        if (firstRegister == analyzedInstruction.postRegisterMap.length - 1) {
2092            throw new ValidationException("v" + firstRegister + " is the last register and not a valid wide register " +
2093                    "pair.");
2094        }
2095
2096        RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(firstRegister);
2097        assert registerType != null;
2098        if (registerType.category == RegisterType.Category.Unknown) {
2099            return registerType;
2100        }
2101        checkRegister(registerType, WideLowCategories);
2102
2103        RegisterType secondRegisterType = analyzedInstruction.getPreInstructionRegisterType(firstRegister + 1);
2104        assert secondRegisterType != null;
2105        checkRegister(secondRegisterType, WideHighCategories);
2106
2107        if ((       registerType.category == RegisterType.Category.LongLo &&
2108                    secondRegisterType.category == RegisterType.Category.DoubleHi)
2109            ||  (   registerType.category == RegisterType.Category.DoubleLo &&
2110                    secondRegisterType.category == RegisterType.Category.LongHi)) {
2111            assert false;
2112            throw new ValidationException("The first register in the wide register pair isn't the same type (long " +
2113                    "vs. double) as the second register in the pair");
2114        }
2115
2116        return registerType;
2117    }
2118}
2119