MethodAnalyzer.java revision 92616c9f60a30b5d5ac423675db732cb2428ce79
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        }
558
559        assert false;
560        return false;
561    }
562
563    private static final EnumSet<RegisterType.Category> Primitive32BitCategories = EnumSet.of(
564            RegisterType.Category.Null,
565            RegisterType.Category.Boolean,
566            RegisterType.Category.Byte,
567            RegisterType.Category.Short,
568            RegisterType.Category.Char,
569            RegisterType.Category.Integer,
570            RegisterType.Category.Float);
571
572    private static final EnumSet<RegisterType.Category> WideLowCategories = EnumSet.of(
573            RegisterType.Category.LongLo,
574            RegisterType.Category.DoubleLo);
575
576    private static final EnumSet<RegisterType.Category> WideHighCategories = EnumSet.of(
577            RegisterType.Category.LongHi,
578            RegisterType.Category.DoubleHi);
579
580    private static final EnumSet<RegisterType.Category> ReferenceCategories = EnumSet.of(
581            RegisterType.Category.Null,
582            RegisterType.Category.Reference);
583
584    private boolean handleMove(AnalyzedInstruction analyzedInstruction,
585                               EnumSet<RegisterType.Category> allowedCategories) {
586        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
587
588        //get the "pre-instruction" register type for the source register
589        RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
590        assert sourceRegisterType != null;
591
592        if (sourceRegisterType.category == RegisterType.Category.Unknown) {
593            //we don't know the source register type yet, so we can't verify it. Return false, and we'll come back later
594            return false;
595        }
596
597        checkRegister(sourceRegisterType, allowedCategories);
598
599        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, sourceRegisterType);
600        return true;
601    }
602
603    private boolean handleMoveWide(AnalyzedInstruction analyzedInstruction) {
604        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
605
606        RegisterType sourceRegisterType = getAndCheckWideSourcePair(analyzedInstruction,
607                instruction.getRegisterB());
608        assert sourceRegisterType != null;
609
610        if (sourceRegisterType.category == RegisterType.Category.Unknown) {
611            //we don't know the source register type yet, so we can't verify it. Return false, and we'll come back later
612            return false;
613        }
614
615        checkWideDestinationPair(analyzedInstruction);
616
617        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, sourceRegisterType);
618        return true;
619    }
620
621    private boolean handleMoveResult(AnalyzedInstruction analyzedInstruction,
622                                     EnumSet<RegisterType.Category> allowedCategories) {
623
624        //TODO: handle the case when the previous instruction is an odexed instruction
625
626        if (analyzedInstruction.instructionIndex == 0) {
627            throw new ValidationException(analyzedInstruction.instruction.opcode.name + " cannot be the first " +
628                    "instruction in a method. It must occur after an invoke-*/fill-new-array instruction");
629        }
630
631        AnalyzedInstruction previousInstruction = instructions.valueAt(analyzedInstruction.instructionIndex-1);
632
633        if (!previousInstruction.instruction.opcode.setsResult()) {
634            throw new ValidationException(analyzedInstruction.instruction.opcode.name + " must occur after an " +
635                    "invoke-*/fill-new-array instruction");
636        }
637
638        if (analyzedInstruction.instruction.opcode.setsWideRegister()) {
639            checkWideDestinationPair(analyzedInstruction);
640        }
641
642        //TODO: does dalvik allow a move-result after an invoke with a void return type?
643        RegisterType destinationRegisterType;
644
645        InstructionWithReference invokeInstruction = (InstructionWithReference)previousInstruction.instruction;
646        Item item = invokeInstruction.getReferencedItem();
647
648        if (item instanceof MethodIdItem) {
649            destinationRegisterType = RegisterType.getRegisterTypeForTypeIdItem(
650                    ((MethodIdItem)item).getPrototype().getReturnType());
651        } else {
652            assert item instanceof TypeIdItem;
653            destinationRegisterType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item);
654        }
655
656        checkRegister(destinationRegisterType, allowedCategories);
657        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, destinationRegisterType);
658        return true;
659    }
660
661    private boolean handleMoveException(AnalyzedInstruction analyzedInstruction) {
662        CodeItem.TryItem[] tries = encodedMethod.codeItem.getTries();
663        int instructionAddress = getInstructionAddress(analyzedInstruction);
664
665        if (tries == null) {
666            throw new ValidationException("move-exception must be the first instruction in an exception handler block");
667        }
668
669        RegisterType exceptionType = null;
670
671        for (CodeItem.TryItem tryItem: encodedMethod.codeItem.getTries()) {
672            if (tryItem.encodedCatchHandler.getCatchAllHandlerAddress() == instructionAddress) {
673                exceptionType = RegisterType.getRegisterType(RegisterType.Category.Reference,
674                        ClassPath.getClassDef("Ljava/lang/Throwable;"));
675                break;
676            }
677            for (CodeItem.EncodedTypeAddrPair handler: tryItem.encodedCatchHandler.handlers) {
678                if (handler.getHandlerAddress() == instructionAddress) {
679                    exceptionType = RegisterType.getRegisterTypeForTypeIdItem(handler.exceptionType)
680                            .merge(exceptionType);
681                }
682            }
683        }
684
685        //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)
686        checkRegister(exceptionType, ReferenceCategories);
687        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, exceptionType);
688        return true;
689    }
690
691    private boolean checkConstructorReturn(AnalyzedInstruction analyzedInstruction) {
692        assert this.isInstanceConstructor();
693
694        //if we're in an instance constructor (an <init> method), then the superclass <init> must have been called.
695        //When execution enters the method, the "this" register is set as an uninitialized reference to the containing
696        //class. Once the superclass' <init> is called, the "this" register is upgraded to a full-blown reference type,
697        //so we need to ensure that the "this" register isn't an uninitialized reference
698
699        int thisRegister = getThisRegister();
700        RegisterType thisRegisterType = analyzedInstruction.postRegisterMap[thisRegister];
701
702        if (thisRegisterType.category == RegisterType.Category.Unknown) {
703            //we don't have enough information yet, so return false. We'll come back later
704            return false;
705        }
706        if (thisRegisterType.category == RegisterType.Category.UninitRef) {
707            throw new ValidationException("Returning from constructor without calling the superclass' <init>");
708        }
709        assert thisRegisterType.category == RegisterType.Category.Reference;
710        assert thisRegisterType.type == ClassPath.getClassDef(encodedMethod.method.getContainingClass());
711        return true;
712    }
713
714    private boolean handleReturnVoid(AnalyzedInstruction analyzedInstruction) {
715        if (this.isInstanceConstructor()) {
716            if (!checkConstructorReturn(analyzedInstruction)) {
717                return false;
718            }
719        }
720
721        TypeIdItem returnType = encodedMethod.method.getPrototype().getReturnType();
722        if (returnType.getTypeDescriptor().charAt(0) != 'V') {
723            //TODO: could add which return-* variation should be used instead
724            throw new ValidationException("Cannot use return-void with a non-void return type (" +
725                returnType.getTypeDescriptor() + ")");
726        }
727        return true;
728    }
729
730    private boolean handleReturn(AnalyzedInstruction analyzedInstruction) {
731        if (this.isInstanceConstructor()) {
732            if (!checkConstructorReturn(analyzedInstruction)) {
733                return false;
734            }
735        }
736
737        SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
738        RegisterType returnRegisterType = analyzedInstruction.postRegisterMap[instruction.getRegisterA()];
739
740        if (returnRegisterType.category == RegisterType.Category.Unknown) {
741            return false;
742        }
743
744        checkRegister(returnRegisterType, Primitive32BitCategories);
745
746        TypeIdItem returnType = encodedMethod.method.getPrototype().getReturnType();
747        if (returnType.getTypeDescriptor().charAt(0) == 'V') {
748            throw new ValidationException("Cannot use return with a void return type. Use return-void instead");
749        }
750
751        RegisterType registerType = RegisterType.getRegisterTypeForTypeIdItem(returnType);
752
753        if (!Primitive32BitCategories.contains(registerType.category)) {
754            //TODO: could add which return-* variation should be used instead
755            throw new ValidationException("Cannot use return with return type " + returnType.getTypeDescriptor());
756        }
757
758
759        return true;
760    }
761
762    private boolean handleReturnWide(AnalyzedInstruction analyzedInstruction) {
763        if (this.isInstanceConstructor()) {
764            if (!checkConstructorReturn(analyzedInstruction)) {
765                return false;
766            }
767        }
768
769        SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
770        RegisterType returnType = getAndCheckWideSourcePair(analyzedInstruction, instruction.getRegisterA());
771
772        if (returnType.category == RegisterType.Category.Unknown) {
773            return false;
774        }
775
776
777        TypeIdItem returnTypeIdItem = encodedMethod.method.getPrototype().getReturnType();
778        if (returnTypeIdItem.getTypeDescriptor().charAt(0) == 'V') {
779            throw new ValidationException("Cannot use return-wide with a void return type. Use return-void instead");
780        }
781
782        returnType = RegisterType.getRegisterTypeForTypeIdItem(returnTypeIdItem);
783        if (!WideLowCategories.contains(returnType.category)) {
784            //TODO: could add which return-* variation should be used instead
785            throw new ValidationException("Cannot use return-wide with return type " +
786                    returnTypeIdItem.getTypeDescriptor());
787        }
788
789        return true;
790    }
791
792    private boolean handleReturnObject(AnalyzedInstruction analyzedInstruction) {
793        if (this.isInstanceConstructor()) {
794            if (!checkConstructorReturn(analyzedInstruction)) {
795                return false;
796            }
797        }
798
799        SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
800        int returnRegister = instruction.getRegisterA();
801        RegisterType returnRegisterType = analyzedInstruction.postRegisterMap[returnRegister];
802
803        if (returnRegisterType.category == RegisterType.Category.Unknown) {
804            return false;
805        }
806
807        checkRegister(returnRegisterType, ReferenceCategories);
808
809
810        TypeIdItem returnTypeIdItem = encodedMethod.method.getPrototype().getReturnType();
811        if (returnTypeIdItem.getTypeDescriptor().charAt(0) == 'V') {
812            throw new ValidationException("Cannot use return with a void return type. Use return-void instead");
813        }
814
815        RegisterType returnType = RegisterType.getRegisterTypeForTypeIdItem(returnTypeIdItem);
816
817        if (!ReferenceCategories.contains(returnType.category)) {
818            //TODO: could add which return-* variation should be used instead
819            throw new ValidationException("Cannot use " + analyzedInstruction + " with return type " +
820                    returnTypeIdItem.getTypeDescriptor());
821        }
822
823        if (returnType.type.isInterface()) {
824            if (!returnRegisterType.type.implementsInterface(returnType.type)) {
825                //TODO: how to handle warnings?
826            }
827        } else {
828            if (!returnRegisterType.type.extendsClass(returnType.type)) {
829                throw new ValidationException("The return value in register v" + Integer.toString(returnRegister) +
830                        "(" + returnRegisterType.type.getClassType() + ") is not compatible with the method's return " +
831                        "type (" + returnType.type.getClassType() + ")");
832            }
833        }
834
835        return true;
836    }
837
838    private boolean handleConst(AnalyzedInstruction analyzedInstruction) {
839        LiteralInstruction instruction = (LiteralInstruction)analyzedInstruction.instruction;
840
841        RegisterType newDestinationRegisterType = RegisterType.getRegisterTypeForLiteral(instruction.getLiteral());
842
843        //we assume that the literal value is a valid value for the given instruction type, because it's impossible
844        //to store an invalid literal with the instruction. so we don't need to check the type of the literal
845        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, newDestinationRegisterType);
846        return true;
847    }
848
849    private boolean handleConstHigh16(AnalyzedInstruction analyzedInstruction) {
850        LiteralInstruction instruction = (LiteralInstruction)analyzedInstruction.instruction;
851
852        //TODO: test this
853        long literalValue = instruction.getLiteral() << 16;
854        RegisterType newDestinationRegisterType = RegisterType.getRegisterTypeForLiteral(literalValue);
855
856        //we assume that the literal value is a valid value for the given instruction type, because it's impossible
857        //to store an invalid literal with the instruction. so we don't need to check the type of the literal
858        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, newDestinationRegisterType);
859        return true;
860    }
861
862    private boolean handleWideConst(AnalyzedInstruction analyzedInstruction) {
863        setWideDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
864                RegisterType.getRegisterType(RegisterType.Category.LongLo, null));
865        return true;
866    }
867
868    private boolean handleConstString(AnalyzedInstruction analyzedInstruction) {
869        ClassPath.ClassDef stringClassDef = ClassPath.getClassDef("Ljava/lang/String;");
870        RegisterType stringType = RegisterType.getRegisterType(RegisterType.Category.Reference, stringClassDef);
871        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, stringType);
872        return true;
873    }
874
875    private boolean handleConstClass(AnalyzedInstruction analyzedInstruction) {
876        ClassPath.ClassDef classClassDef = ClassPath.getClassDef("Ljava/lang/Class;");
877        RegisterType classType = RegisterType.getRegisterType(RegisterType.Category.Reference, classClassDef);
878
879        InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
880        Item item = instruction.getReferencedItem();
881        assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
882
883        //make sure the referenced class is resolvable
884        //TODO: need to check class access
885        ClassPath.ClassDef classDef = ClassPath.getClassDef((TypeIdItem)item);
886        return false;
887    }
888
889    private boolean handleMonitor(AnalyzedInstruction analyzedInstruction) {
890        SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction;
891
892        RegisterType registerType = analyzedInstruction.postRegisterMap[instruction.getRegisterA()];
893        assert registerType != null;
894        if (registerType.category == RegisterType.Category.Unknown) {
895            return false;
896        }
897
898        checkRegister(registerType, ReferenceCategories);
899        return true;
900    }
901
902    private boolean handleCheckCast(AnalyzedInstruction analyzedInstruction) {
903        {
904            //ensure the "source" register is a reference type
905            SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
906
907            RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA());
908            assert registerType != null;
909            if (registerType.category == RegisterType.Category.Unknown) {
910                return false;
911            }
912
913            checkRegister(registerType, ReferenceCategories);
914        }
915
916        {
917            //resolve and verify the class that we're casting to
918            InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
919
920            Item item = instruction.getReferencedItem();
921            assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
922
923            //TODO: need to check class access
924            RegisterType newDestinationRegisterType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item);
925            try {
926                checkRegister(newDestinationRegisterType, ReferenceCategories);
927            } catch (ValidationException ex) {
928                //TODO: verify that dalvik allows a non-reference type..
929                //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)
930            }
931
932            setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, newDestinationRegisterType);
933            return true;
934        }
935    }
936
937    private boolean handleInstanceOf(AnalyzedInstruction analyzedInstruction) {
938        {
939            //ensure the register that is being checks is a reference type
940            TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction;
941
942            RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
943            assert registerType != null;
944            if (registerType.category == RegisterType.Category.Unknown) {
945                return false;
946            }
947
948            checkRegister(registerType, ReferenceCategories);
949        }
950
951        {
952            //resolve and verify the class that we're checking against
953            InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
954
955            Item item = instruction.getReferencedItem();
956            assert  item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
957            RegisterType registerType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item);
958            checkRegister(registerType, ReferenceCategories);
959
960            //TODO: is it valid to use an array type?
961
962            //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.
963            setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
964                    RegisterType.getRegisterType(RegisterType.Category.Boolean, null));
965            return true;
966        }
967    }
968
969    private boolean handleArrayLength(AnalyzedInstruction analyzedInstruction) {
970        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction;
971
972        int arrayRegisterNumber = instruction.getRegisterB();
973        RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(arrayRegisterNumber);
974        assert arrayRegisterType != null;
975        if (arrayRegisterType.category == RegisterType.Category.Unknown) {
976            return false;
977        }
978
979        assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
980
981        checkRegister(arrayRegisterType, ReferenceCategories);
982        if (arrayRegisterType.type != null) {
983            if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
984                throw new ValidationException("Cannot use array-length with non-array type " +
985                        arrayRegisterType.type.getClassType());
986            }
987        }
988
989        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
990                RegisterType.getRegisterType(RegisterType.Category.Integer, null));
991        return true;
992    }
993
994    private boolean handleNewInstance(AnalyzedInstruction analyzedInstruction) {
995        InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
996
997        Item item = instruction.getReferencedItem();
998        assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
999
1000        //TODO: need to check class access
1001        RegisterType classType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item);
1002        checkRegister(classType, ReferenceCategories);
1003        if (((TypeIdItem)item).getTypeDescriptor().charAt(0) == '[') {
1004            throw new ValidationException("Cannot use array type \"" + ((TypeIdItem)item).getTypeDescriptor() +
1005                    "\" with new-instance. Use new-array instead.");
1006        }
1007
1008        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1009                RegisterType.getRegisterType(RegisterType.Category.UninitRef, classType.type));
1010        return true;
1011    }
1012
1013    private boolean handleNewArray(AnalyzedInstruction analyzedInstruction) {
1014        {
1015            TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
1016
1017            int sizeRegister = instruction.getRegisterB();
1018            RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(sizeRegister);
1019            assert registerType != null;
1020
1021            if (registerType.category == RegisterType.Category.Unknown) {
1022                return false;
1023            }
1024
1025            checkRegister(registerType, Primitive32BitCategories);
1026        }
1027
1028        InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
1029
1030        Item item = instruction.getReferencedItem();
1031        assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
1032
1033        RegisterType arrayType = RegisterType.getRegisterTypeForTypeIdItem((TypeIdItem)item);
1034        assert arrayType.type instanceof ClassPath.ArrayClassDef;
1035
1036        checkRegister(arrayType, ReferenceCategories);
1037        if (arrayType.type.getClassType().charAt(0) != '[') {
1038            throw new ValidationException("Cannot use non-array type \"" + arrayType.type.getClassType() +
1039                    "\" with new-array. Use new-instance instead.");
1040        }
1041
1042        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, arrayType);
1043        return true;
1044    }
1045
1046    private static interface RegisterIterator {
1047        int getRegister();
1048        boolean moveNext();
1049    }
1050
1051    private boolean handleFilledNewArrayCommon(AnalyzedInstruction analyzedInstruction,
1052                                               RegisterIterator registerIterator) {
1053        InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
1054
1055        RegisterType arrayType;
1056        RegisterType arrayImmediateElementType;
1057
1058        Item item = instruction.getReferencedItem();
1059        assert  item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
1060
1061        ClassPath.ClassDef classDef = ClassPath.getClassDef((TypeIdItem)item);
1062
1063        if (classDef.getClassType().charAt(0) != '[') {
1064            throw new ValidationException("Cannot use non-array type \"" + classDef.getClassType() +
1065                "\" with new-array. Use new-instance instead.");
1066        }
1067
1068        ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)classDef;
1069        arrayType = RegisterType.getRegisterType(RegisterType.Category.Reference, classDef);
1070        arrayImmediateElementType = RegisterType.getRegisterTypeForType(
1071                arrayClassDef.getImmediateElementClass().getClassType());
1072        String baseElementType = arrayClassDef.getBaseElementClass().getClassType();
1073        if (baseElementType.charAt(0) == 'J' || baseElementType.charAt(0) == 'D') {
1074            throw new ValidationException("Cannot use filled-new-array to create an array of wide values " +
1075                    "(long or double)");
1076        }
1077
1078        do {
1079            int register = registerIterator.getRegister();
1080            RegisterType elementType = analyzedInstruction.getPreInstructionRegisterType(register);
1081            assert elementType != null;
1082
1083            if (elementType.category == RegisterType.Category.Unknown) {
1084                return false;
1085            }
1086
1087            if (!elementType.canBeAssignedTo(arrayImmediateElementType)) {
1088                throw new ValidationException("Register v" + Integer.toString(register) + " is of type " +
1089                        elementType.toString() + " and is incompatible with the array type " +
1090                        arrayType.type.getClassType());
1091            }
1092        } while (registerIterator.moveNext());
1093
1094        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, arrayType);
1095        return true;
1096    }
1097
1098    private boolean handleFilledNewArray(AnalyzedInstruction analyzedInstruction) {
1099        FiveRegisterInstruction instruction = (FiveRegisterInstruction)analyzedInstruction.instruction;
1100        final int registerCount = instruction.getRegCount();
1101        final int[] registers = new int[]{instruction.getRegisterD(), instruction.getRegisterE(),
1102                                          instruction.getRegisterF(), instruction.getRegisterG(),
1103                                          instruction.getRegisterA()};
1104
1105        return handleFilledNewArrayCommon(analyzedInstruction,
1106                new RegisterIterator() {
1107                    private int currentRegister = 0;
1108                    public int getRegister() {
1109                        return registers[currentRegister];
1110                    }
1111
1112                    public boolean moveNext() {
1113                        currentRegister++;
1114                        if (currentRegister >= registerCount) {
1115                            return false;
1116                        }
1117                        return true;
1118                    }
1119                });
1120    }
1121
1122    private boolean handleFilledNewArrayRange(AnalyzedInstruction analyzedInstruction) {
1123        final RegisterRangeInstruction instruction = (RegisterRangeInstruction)analyzedInstruction.instruction;
1124
1125        //instruction.getStartRegister() and instruction.getRegCount() both return an int value, but are actually
1126        //unsigned 16 bit values, so we don't have to worry about overflowing an int when adding them together
1127        if (instruction.getStartRegister() + instruction.getRegCount() >= 1<<16) {
1128            throw new ValidationException(String.format("Invalid register range {v%d .. v%d}. The ending register " +
1129                    "is larger than the largest allowed register of v65535.",
1130                    instruction.getStartRegister(),
1131                    instruction.getStartRegister() + instruction.getRegCount() - 1));
1132        }
1133
1134        return handleFilledNewArrayCommon(analyzedInstruction,
1135                new RegisterIterator() {
1136                    private int currentRegister = 0;
1137                    private final int startRegister = instruction.getStartRegister();
1138                    private final int registerCount = instruction.getRegCount();
1139
1140                    public int getRegister() {
1141                        return startRegister + currentRegister;
1142                    }
1143
1144                    public boolean moveNext() {
1145                        currentRegister++;
1146                        if (currentRegister >= registerCount) {
1147                            return false;
1148                        }
1149                        return true;
1150                    }
1151                });
1152    }
1153
1154    private boolean handleFillArrayData(AnalyzedInstruction analyzedInstruction) {
1155        SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
1156
1157        int register = instruction.getRegisterA();
1158        RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(register);
1159        assert registerType != null;
1160
1161        if (registerType.category == RegisterType.Category.Unknown ||
1162            registerType.category == RegisterType.Category.Null) {
1163            return false;
1164        }
1165
1166        if (registerType.category != RegisterType.Category.Reference) {
1167            throw new ValidationException(String.format("Cannot use fill-array-data with non-array register v%d of " +
1168                    "type %s", register, registerType.toString()));
1169        }
1170
1171        assert registerType.type instanceof ClassPath.ArrayClassDef;
1172        ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)registerType.type;
1173
1174        if (arrayClassDef.getArrayDimensions() != 1) {
1175            throw new ValidationException(String.format("Cannot use fill-array-data with array type %s. It can only " +
1176                    "be used with a one-dimensional array of primitives.", arrayClassDef.getClassType()));
1177        }
1178
1179        int elementWidth;
1180        switch (arrayClassDef.getBaseElementClass().getClassType().charAt(0)) {
1181            case 'Z':
1182            case 'B':
1183                elementWidth = 1;
1184                break;
1185            case 'C':
1186            case 'S':
1187                elementWidth = 2;
1188                break;
1189            case 'I':
1190            case 'F':
1191                elementWidth = 4;
1192                break;
1193            case 'J':
1194            case 'D':
1195                elementWidth = 8;
1196                break;
1197            default:
1198                throw new ValidationException(String.format("Cannot use fill-array-data with array type %s. It can " +
1199                        "only be used with a one-dimensional array of primitives.", arrayClassDef.getClassType()));
1200        }
1201
1202
1203        int arrayDataAddressOffset = ((OffsetInstruction)analyzedInstruction.instruction).getTargetAddressOffset();
1204        int arrayDataCodeAddress = getInstructionAddress(analyzedInstruction) + arrayDataAddressOffset;
1205        AnalyzedInstruction arrayDataInstruction = this.instructions.get(arrayDataCodeAddress);
1206        if (arrayDataInstruction == null || arrayDataInstruction.instruction.getFormat() != Format.ArrayData) {
1207            throw new ValidationException(String.format("Could not find an array data structure at code address 0x%x",
1208                    arrayDataCodeAddress));
1209        }
1210
1211        ArrayDataPseudoInstruction arrayDataPseudoInstruction =
1212                (ArrayDataPseudoInstruction)arrayDataInstruction.instruction;
1213
1214        if (elementWidth != arrayDataPseudoInstruction.getElementWidth()) {
1215            throw new ValidationException(String.format("The array data at code address 0x%x does not have the " +
1216                    "correct element width for array type %s. Expecting element width %d, got element width %d.",
1217                    arrayDataCodeAddress, arrayClassDef.getClassType(), elementWidth,
1218                    arrayDataPseudoInstruction.getElementWidth()));
1219        }
1220
1221        return true;
1222    }
1223
1224    private boolean handleThrow(AnalyzedInstruction analyzedInstruction) {
1225        int register = ((SingleRegisterInstruction)analyzedInstruction.instruction).getRegisterA();
1226
1227        RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(register);
1228        assert registerType != null;
1229
1230        if (registerType.category == RegisterType.Category.Unknown) {
1231            return false;
1232        }
1233
1234        if (registerType.category == RegisterType.Category.Null) {
1235            return true;
1236        }
1237
1238        if (registerType.category != RegisterType.Category.Reference) {
1239            throw new ValidationException(String.format("Cannot use throw with non-reference type %s in register v%d",
1240                    registerType.toString(), register));
1241        }
1242
1243        assert registerType.type != null;
1244
1245        if (!registerType.type.extendsClass(ClassPath.getClassDef("Ljava/lang/Throwable;"))) {
1246            throw new ValidationException(String.format("Cannot use throw with non-throwable type %s in register v%d",
1247                    registerType.type.getClassType(), register));
1248        }
1249
1250        return true;
1251    }
1252
1253    private boolean handleSwitch(AnalyzedInstruction analyzedInstruction, Format expectedSwitchDataFormat) {
1254        int register = ((SingleRegisterInstruction)analyzedInstruction.instruction).getRegisterA();
1255        int switchCodeAddressOffset = ((OffsetInstruction)analyzedInstruction.instruction).getTargetAddressOffset();
1256
1257        RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(register);
1258        assert registerType != null;
1259
1260        if (registerType.category == RegisterType.Category.Unknown) {
1261            return false;
1262        }
1263
1264        checkRegister(registerType, Primitive32BitCategories);
1265
1266        int switchDataCodeAddress = this.getInstructionAddress(analyzedInstruction) + switchCodeAddressOffset;
1267        AnalyzedInstruction switchDataAnalyzedInstruction = instructions.get(switchDataCodeAddress);
1268
1269        if (switchDataAnalyzedInstruction == null ||
1270            switchDataAnalyzedInstruction.instruction.getFormat() != expectedSwitchDataFormat) {
1271            throw new ValidationException(String.format("There is no %s structure at code address 0x%x",
1272                    expectedSwitchDataFormat.name(), switchDataCodeAddress));
1273        }
1274
1275        return true;
1276    }
1277
1278    private boolean handleFloatCmp(AnalyzedInstruction analyzedInstruction) {
1279        ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
1280
1281        RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1282        assert registerType != null;
1283
1284        if (registerType.category == RegisterType.Category.Unknown) {
1285            return false;
1286        }
1287        checkRegister(registerType, Primitive32BitCategories);
1288
1289        registerType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterC());
1290        assert registerType != null;
1291
1292        if (registerType.category == RegisterType.Category.Unknown) {
1293            return false;
1294        }
1295        checkRegister(registerType, Primitive32BitCategories);
1296
1297        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1298                RegisterType.getRegisterType(RegisterType.Category.Byte, null));
1299        return true;
1300    }
1301
1302    private boolean handleWideCmp(AnalyzedInstruction analyzedInstruction) {
1303        ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
1304
1305        RegisterType registerType = getAndCheckWideSourcePair(analyzedInstruction, instruction.getRegisterB());
1306        assert registerType != null;
1307        if (registerType.category == RegisterType.Category.Unknown) {
1308            return false;
1309        }
1310
1311        registerType = getAndCheckWideSourcePair(analyzedInstruction, instruction.getRegisterC());
1312        assert registerType != null;
1313        if (registerType.category == RegisterType.Category.Unknown) {
1314            return false;
1315        }
1316
1317        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1318                RegisterType.getRegisterType(RegisterType.Category.Byte, null));
1319        return true;
1320    }
1321
1322    private boolean handleIfEqNe(AnalyzedInstruction analyzedInstruction) {
1323        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
1324
1325        RegisterType registerType1 = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA());
1326        assert registerType1 != null;
1327        if (registerType1.category == RegisterType.Category.Unknown) {
1328            return false;
1329        }
1330
1331        RegisterType registerType2 = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1332        assert registerType2 != null;
1333        if (registerType2.category == RegisterType.Category.Unknown) {
1334            return false;
1335        }
1336
1337        if (!(
1338                (ReferenceCategories.contains(registerType1.category) &&
1339                ReferenceCategories.contains(registerType2.category))
1340                    ||
1341                (Primitive32BitCategories.contains(registerType1.category) &&
1342                Primitive32BitCategories.contains(registerType2.category))
1343              )) {
1344
1345            throw new ValidationException(String.format("%s cannot be used on registers of dissimilar types %s and " +
1346                    "%s. They must both be a reference type or a primitive 32 bit type.",
1347                    analyzedInstruction.instruction.opcode.name, registerType1.toString(), registerType2.toString()));
1348        }
1349
1350        return true;
1351    }
1352
1353    private boolean handleIf(AnalyzedInstruction analyzedInstruction) {
1354        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
1355
1356        RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA());
1357        assert registerType != null;
1358
1359        if (registerType.category == RegisterType.Category.Unknown) {
1360            return false;
1361        }
1362        checkRegister(registerType, Primitive32BitCategories);
1363
1364        registerType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1365        assert registerType != null;
1366
1367        if (registerType.category == RegisterType.Category.Unknown) {
1368            return false;
1369        }
1370        checkRegister(registerType, Primitive32BitCategories);
1371
1372        return true;
1373    }
1374
1375    private boolean handleIfEqzNez(AnalyzedInstruction analyzedInstruction) {
1376        SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
1377
1378        RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA());
1379        assert registerType != null;
1380        if (registerType.category == RegisterType.Category.Unknown) {
1381            return false;
1382        }
1383
1384        if (!ReferenceCategories.contains(registerType.category) &&
1385            !Primitive32BitCategories.contains(registerType.category)) {
1386            throw new ValidationException(String.format("%s cannot be used with register type %s. Expecting 32-bit " +
1387                    "primitive type or reference type.", analyzedInstruction.instruction.opcode));
1388        }
1389
1390        return true;
1391    }
1392
1393    private boolean handleIfz(AnalyzedInstruction analyzedInstruction) {
1394        SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
1395
1396        RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA());
1397        assert registerType != null;
1398
1399        if (registerType.category == RegisterType.Category.Unknown) {
1400            return false;
1401        }
1402        checkRegister(registerType, Primitive32BitCategories);
1403
1404        return true;
1405    }
1406
1407    private boolean handle32BitPrimitiveAget(AnalyzedInstruction analyzedInstruction,
1408                                             RegisterType.Category instructionCategory) {
1409        ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
1410
1411        RegisterType indexRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterC());
1412        assert indexRegisterType != null;
1413        if (indexRegisterType.category == RegisterType.Category.Unknown) {
1414            return false;
1415        }
1416        checkRegister(indexRegisterType, Primitive32BitCategories);
1417
1418        RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1419        assert arrayRegisterType != null;
1420        if (indexRegisterType.category == RegisterType.Category.Unknown) {
1421            return false;
1422        }
1423
1424        if (arrayRegisterType.category != RegisterType.Category.Null) {
1425            if (arrayRegisterType.category != RegisterType.Category.Reference) {
1426                throw new ValidationException(String.format("Cannot use %s with non-array type %s",
1427                        analyzedInstruction.instruction.opcode.name, arrayRegisterType.category.toString()));
1428            }
1429
1430            assert arrayRegisterType.type != null;
1431            if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
1432                throw new ValidationException(String.format("Cannot use %s with non-array type %s",
1433                        analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType()));
1434            }
1435
1436            assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
1437            ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type;
1438
1439            if (arrayClassDef.getArrayDimensions() != 1) {
1440                throw new ValidationException(String.format("Cannot use %s with multi-dimensional array type %s",
1441                        analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType()));
1442            }
1443
1444            RegisterType arrayBaseType =
1445                    RegisterType.getRegisterTypeForType(arrayClassDef.getBaseElementClass().getClassType());
1446            if (checkArrayFieldAssignment(arrayBaseType.category, instructionCategory)) {
1447                throw new ValidationException(String.format("Cannot use %s with array type %s. Incorrect array type " +
1448                        "for the instruction.", analyzedInstruction.instruction.opcode.name,
1449                        arrayRegisterType.type.getClassType()));
1450            }
1451        }
1452
1453        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1454                RegisterType.getRegisterType(instructionCategory, null));
1455
1456        return true;
1457    }
1458
1459    private boolean handleAgetWide(AnalyzedInstruction analyzedInstruction) {
1460        ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
1461
1462        RegisterType indexRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterC());
1463        assert indexRegisterType != null;
1464        if (indexRegisterType.category == RegisterType.Category.Unknown) {
1465            return false;
1466        }
1467        checkRegister(indexRegisterType, Primitive32BitCategories);
1468
1469        RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1470        assert arrayRegisterType != null;
1471        if (indexRegisterType.category == RegisterType.Category.Unknown) {
1472            return false;
1473        }
1474
1475        if (arrayRegisterType.category != RegisterType.Category.Null) {
1476            if (arrayRegisterType.category != RegisterType.Category.Reference) {
1477                throw new ValidationException(String.format("Cannot use aget-wide with non-array type %s",
1478                        arrayRegisterType.category.toString()));
1479            }
1480
1481            assert arrayRegisterType.type != null;
1482            if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
1483                throw new ValidationException(String.format("Cannot use aget-wide with non-array type %s",
1484                        arrayRegisterType.type.getClassType()));
1485            }
1486
1487            assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
1488            ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type;
1489
1490            if (arrayClassDef.getArrayDimensions() != 1) {
1491                throw new ValidationException(String.format("Cannot use aget-wide with multi-dimensional array type %s",
1492                        arrayRegisterType.type.getClassType()));
1493            }
1494
1495            char arrayBaseType = arrayClassDef.getBaseElementClass().getClassType().charAt(0);
1496            if (arrayBaseType == 'J') {
1497                setWideDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1498                        RegisterType.getRegisterType(RegisterType.Category.LongLo, null));
1499            } else if (arrayBaseType == 'D') {
1500                setWideDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1501                        RegisterType.getRegisterType(RegisterType.Category.DoubleLo, null));
1502            } else {
1503                throw new ValidationException(String.format("Cannot use aget-wide with array type %s. Incorrect " +
1504                        "array type for the instruction.", arrayRegisterType.type.getClassType()));
1505            }
1506        } else {
1507            setWideDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1508                        RegisterType.getRegisterType(RegisterType.Category.LongLo, null));
1509        }
1510
1511        return true;
1512    }
1513
1514    private boolean handleAgetObject(AnalyzedInstruction analyzedInstruction) {
1515        ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
1516
1517        RegisterType indexRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterC());
1518        assert indexRegisterType != null;
1519        if (indexRegisterType.category == RegisterType.Category.Unknown) {
1520            return false;
1521        }
1522        checkRegister(indexRegisterType, Primitive32BitCategories);
1523
1524        RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1525        assert arrayRegisterType != null;
1526        if (indexRegisterType.category == RegisterType.Category.Unknown) {
1527            return false;
1528        }
1529
1530        if (arrayRegisterType.category != RegisterType.Category.Null) {
1531            if (arrayRegisterType.category != RegisterType.Category.Reference) {
1532                throw new ValidationException(String.format("Cannot use aget-object with non-array type %s",
1533                        arrayRegisterType.category.toString()));
1534            }
1535
1536            assert arrayRegisterType.type != null;
1537            if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
1538                throw new ValidationException(String.format("Cannot use aget-object with non-array type %s",
1539                        arrayRegisterType.type.getClassType()));
1540            }
1541
1542            assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
1543            ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type;
1544
1545            ClassPath.ClassDef elementClassDef = arrayClassDef.getImmediateElementClass();
1546            char elementTypePrefix = elementClassDef.getClassType().charAt(0);
1547            if (elementTypePrefix != 'L' && elementTypePrefix != '[') {
1548                throw new ValidationException(String.format("Cannot use aget-object with array type %s. Incorrect " +
1549                        "array type for the instruction.", arrayRegisterType.type.getClassType()));
1550            }
1551
1552            setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1553                    RegisterType.getRegisterType(RegisterType.Category.Reference, elementClassDef));
1554        } else {
1555            setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1556                    RegisterType.getRegisterType(RegisterType.Category.Null, null));
1557        }
1558
1559        return true;
1560    }
1561
1562    private boolean handle32BitPrimitiveAput(AnalyzedInstruction analyzedInstruction,
1563                                             RegisterType.Category instructionCategory) {
1564        ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
1565
1566        RegisterType indexRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterC());
1567        assert indexRegisterType != null;
1568        if (indexRegisterType.category == RegisterType.Category.Unknown) {
1569            return false;
1570        }
1571        checkRegister(indexRegisterType, Primitive32BitCategories);
1572
1573
1574        RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA());
1575        assert sourceRegisterType != null;
1576        if (sourceRegisterType.category == RegisterType.Category.Unknown) {
1577            return false;
1578        }
1579        RegisterType instructionRegisterType = RegisterType.getRegisterType(instructionCategory, null);
1580        if (!sourceRegisterType.canBeAssignedTo(instructionRegisterType)) {
1581            throw new ValidationException(String.format("Cannot use %s with source register type %s.",
1582                    analyzedInstruction.instruction.opcode.name, sourceRegisterType.toString()));
1583        }
1584
1585
1586        RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1587        assert arrayRegisterType != null;
1588        if (indexRegisterType.category == RegisterType.Category.Unknown) {
1589            return false;
1590        }
1591
1592        if (arrayRegisterType.category != RegisterType.Category.Null) {
1593            if (arrayRegisterType.category != RegisterType.Category.Reference) {
1594                throw new ValidationException(String.format("Cannot use %s with non-array type %s",
1595                        analyzedInstruction.instruction.opcode.name, arrayRegisterType.category.toString()));
1596            }
1597
1598            assert arrayRegisterType.type != null;
1599            if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
1600                throw new ValidationException(String.format("Cannot use %s with non-array type %s",
1601                        analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType()));
1602            }
1603
1604            assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
1605            ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type;
1606
1607            if (arrayClassDef.getArrayDimensions() != 1) {
1608                throw new ValidationException(String.format("Cannot use %s with multi-dimensional array type %s",
1609                        analyzedInstruction.instruction.opcode.name, arrayRegisterType.type.getClassType()));
1610            }
1611
1612            RegisterType arrayBaseType =
1613                    RegisterType.getRegisterTypeForType(arrayClassDef.getBaseElementClass().getClassType());
1614            if (checkArrayFieldAssignment(arrayBaseType.category, instructionCategory)) {
1615                throw new ValidationException(String.format("Cannot use %s with array type %s. Incorrect array type " +
1616                        "for the instruction.", analyzedInstruction.instruction.opcode.name,
1617                        arrayRegisterType.type.getClassType()));
1618            }
1619        }
1620
1621        return true;
1622    }
1623
1624    private boolean handleAputWide(AnalyzedInstruction analyzedInstruction) {
1625        ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
1626
1627        RegisterType indexRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterC());
1628        assert indexRegisterType != null;
1629        if (indexRegisterType.category == RegisterType.Category.Unknown) {
1630            return false;
1631        }
1632        checkRegister(indexRegisterType, Primitive32BitCategories);
1633
1634        RegisterType sourceRegisterType = getAndCheckWideSourcePair(analyzedInstruction, instruction.getRegisterA());
1635        if (sourceRegisterType.category == RegisterType.Category.Unknown) {
1636            return false;
1637        }
1638
1639        RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1640        assert arrayRegisterType != null;
1641        if (indexRegisterType.category == RegisterType.Category.Unknown) {
1642            return false;
1643        }
1644
1645        if (arrayRegisterType.category != RegisterType.Category.Null) {
1646            if (arrayRegisterType.category != RegisterType.Category.Reference) {
1647                throw new ValidationException(String.format("Cannot use aput-wide with non-array type %s",
1648                        arrayRegisterType.category.toString()));
1649            }
1650
1651            assert arrayRegisterType.type != null;
1652            if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
1653                throw new ValidationException(String.format("Cannot use aput-wide with non-array type %s",
1654                        arrayRegisterType.type.getClassType()));
1655            }
1656
1657            assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
1658            ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type;
1659
1660            if (arrayClassDef.getArrayDimensions() != 1) {
1661                throw new ValidationException(String.format("Cannot use aput-wide with multi-dimensional array type %s",
1662                        arrayRegisterType.type.getClassType()));
1663            }
1664
1665            char arrayBaseType = arrayClassDef.getBaseElementClass().getClassType().charAt(0);
1666            if (arrayBaseType != 'J' && arrayBaseType != 'D') {
1667                throw new ValidationException(String.format("Cannot use aput-wide with array type %s. Incorrect " +
1668                        "array type for the instruction.", arrayRegisterType.type.getClassType()));
1669            }
1670        }
1671
1672        return true;
1673    }
1674
1675    private boolean handleAputObject(AnalyzedInstruction analyzedInstruction) {
1676        ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
1677
1678        RegisterType indexRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterC());
1679        assert indexRegisterType != null;
1680        if (indexRegisterType.category == RegisterType.Category.Unknown) {
1681            return false;
1682        }
1683        checkRegister(indexRegisterType, Primitive32BitCategories);
1684
1685        RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA());
1686        assert sourceRegisterType != null;
1687        if (sourceRegisterType.category == RegisterType.Category.Unknown) {
1688            return false;
1689        }
1690
1691        RegisterType arrayRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1692        assert arrayRegisterType != null;
1693        if (indexRegisterType.category == RegisterType.Category.Unknown) {
1694            return false;
1695        }
1696
1697        if (arrayRegisterType.category != RegisterType.Category.Null) {
1698            //don't check the source type against the array type, just make sure it is an array of reference types
1699
1700            if (arrayRegisterType.category != RegisterType.Category.Reference) {
1701                throw new ValidationException(String.format("Cannot use aget-object with non-array type %s",
1702                        arrayRegisterType.category.toString()));
1703            }
1704
1705            assert arrayRegisterType.type != null;
1706            if (arrayRegisterType.type.getClassType().charAt(0) != '[') {
1707                throw new ValidationException(String.format("Cannot use aget-object with non-array type %s",
1708                        arrayRegisterType.type.getClassType()));
1709            }
1710
1711            assert arrayRegisterType.type instanceof ClassPath.ArrayClassDef;
1712            ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)arrayRegisterType.type;
1713
1714            ClassPath.ClassDef elementClassDef = arrayClassDef.getImmediateElementClass();
1715            char elementTypePrefix = elementClassDef.getClassType().charAt(0);
1716            if (elementTypePrefix != 'L' && elementTypePrefix != '[') {
1717                throw new ValidationException(String.format("Cannot use aget-object with array type %s. Incorrect " +
1718                        "array type for the instruction.", arrayRegisterType.type.getClassType()));
1719            }
1720        }
1721
1722        return true;
1723    }
1724
1725    private boolean handle32BitPrimitiveIget(AnalyzedInstruction analyzedInstruction,
1726                                             RegisterType.Category instructionCategory) {
1727        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
1728
1729        RegisterType objectRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1730        assert objectRegisterType != null;
1731        if (objectRegisterType.category == RegisterType.Category.Unknown) {
1732            return false;
1733        }
1734        checkRegister(objectRegisterType, ReferenceCategories);
1735
1736        //TODO: check access
1737        //TODO: allow an uninitialized "this" reference, if the current method is an <init> method
1738        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
1739        assert referencedItem instanceof FieldIdItem;
1740        FieldIdItem field = (FieldIdItem)referencedItem;
1741
1742        if (objectRegisterType.category != RegisterType.Category.Null &&
1743            !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) {
1744            throw new ValidationException(String.format("Cannot access field %s through type %s",
1745                    field.getFieldString(), objectRegisterType.type.getClassType()));
1746        }
1747
1748        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
1749
1750        if (!checkArrayFieldAssignment(fieldType.category, instructionCategory)) {
1751                throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
1752                        "for the instruction.", analyzedInstruction.instruction.opcode.name,
1753                        field.getFieldString()));
1754        }
1755
1756        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
1757                RegisterType.getRegisterType(instructionCategory, null));
1758
1759        return true;
1760    }
1761
1762    private boolean handleIgetWide(AnalyzedInstruction analyzedInstruction) {
1763        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
1764
1765        RegisterType objectRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1766        assert objectRegisterType != null;
1767        if (objectRegisterType.category == RegisterType.Category.Unknown) {
1768            return false;
1769        }
1770        checkRegister(objectRegisterType, ReferenceCategories);
1771
1772        getAndCheckWideSourcePair(analyzedInstruction, instruction.getRegisterB());
1773
1774        //TODO: check access
1775        //TODO: allow an uninitialized "this" reference, if the current method is an <init> method
1776        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
1777        assert referencedItem instanceof FieldIdItem;
1778        FieldIdItem field = (FieldIdItem)referencedItem;
1779
1780        if (objectRegisterType.category != RegisterType.Category.Null &&
1781            !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) {
1782            throw new ValidationException(String.format("Cannot access field %s through type %s",
1783                    field.getFieldString(), objectRegisterType.type.getClassType()));
1784        }
1785
1786        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
1787
1788        try {
1789            checkRegister(fieldType, WideLowCategories);
1790        } catch (ValidationException ex) {
1791            throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
1792                        "for the instruction.", analyzedInstruction.instruction.opcode.name,
1793                        field.getFieldString()));
1794        }
1795
1796        setWideDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, fieldType);
1797
1798        return true;
1799    }
1800
1801    private boolean handleIgetObject(AnalyzedInstruction analyzedInstruction) {
1802        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
1803
1804        RegisterType objectRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1805        assert objectRegisterType != null;
1806        if (objectRegisterType.category == RegisterType.Category.Unknown) {
1807            return false;
1808        }
1809        checkRegister(objectRegisterType, ReferenceCategories);
1810
1811        //TODO: check access
1812        //TODO: allow an uninitialized "this" reference, if the current method is an <init> method
1813        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
1814        assert referencedItem instanceof FieldIdItem;
1815        FieldIdItem field = (FieldIdItem)referencedItem;
1816
1817        if (objectRegisterType.category != RegisterType.Category.Null &&
1818            !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) {
1819            throw new ValidationException(String.format("Cannot access field %s through type %s",
1820                    field.getFieldString(), objectRegisterType.type.getClassType()));
1821        }
1822
1823        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
1824
1825        if (fieldType.category != RegisterType.Category.Reference) {
1826            throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
1827                        "for the instruction.", analyzedInstruction.instruction.opcode.name,
1828                        field.getFieldString()));
1829        }
1830
1831        setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, fieldType);
1832
1833        return true;
1834    }
1835
1836    private boolean handle32BitPrimitiveIput(AnalyzedInstruction analyzedInstruction,
1837                                             RegisterType.Category instructionCategory) {
1838        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
1839
1840        RegisterType objectRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1841        assert objectRegisterType != null;
1842        if (objectRegisterType.category == RegisterType.Category.Unknown) {
1843            return false;
1844        }
1845        checkRegister(objectRegisterType, ReferenceCategories);
1846
1847        RegisterType sourceRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterA());
1848        assert sourceRegisterType != null;
1849        if (sourceRegisterType.category == RegisterType.Category.Unknown) {
1850            return false;
1851        }
1852
1853        //per CodeVerify.c in dalvik:
1854        //java generates synthetic functions that write byte values into boolean fields
1855        if (sourceRegisterType.category == RegisterType.Category.Byte &&
1856            instructionCategory == RegisterType.Category.Boolean) {
1857
1858            sourceRegisterType = RegisterType.getRegisterType(RegisterType.Category.Boolean, null);
1859        }
1860
1861        RegisterType instructionRegisterType = RegisterType.getRegisterType(instructionCategory, null);
1862        if (!sourceRegisterType.canBeAssignedTo(instructionRegisterType)) {
1863            throw new ValidationException(String.format("Cannot use %s with source register type %s.",
1864                    analyzedInstruction.instruction.opcode.name, sourceRegisterType.toString()));
1865        }
1866
1867
1868        //TODO: check access
1869        //TODO: allow an uninitialized "this" reference, if the current method is an <init> method
1870        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
1871        assert referencedItem instanceof FieldIdItem;
1872        FieldIdItem field = (FieldIdItem)referencedItem;
1873
1874        if (objectRegisterType.category != RegisterType.Category.Null &&
1875            !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) {
1876            throw new ValidationException(String.format("Cannot access field %s through type %s",
1877                    field.getFieldString(), objectRegisterType.type.getClassType()));
1878        }
1879
1880        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
1881
1882        if (!checkArrayFieldAssignment(fieldType.category, instructionCategory)) {
1883                throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
1884                        "for the instruction.", analyzedInstruction.instruction.opcode.name,
1885                        field.getFieldString()));
1886        }
1887
1888        return true;
1889    }
1890
1891    private boolean handleIputWide(AnalyzedInstruction analyzedInstruction) {
1892        TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
1893
1894        RegisterType objectRegisterType = analyzedInstruction.getPreInstructionRegisterType(instruction.getRegisterB());
1895        assert objectRegisterType != null;
1896        if (objectRegisterType.category == RegisterType.Category.Unknown) {
1897            return false;
1898        }
1899        checkRegister(objectRegisterType, ReferenceCategories);
1900
1901        RegisterType sourceRegisterType = getAndCheckWideSourcePair(analyzedInstruction, instruction.getRegisterA());
1902        assert sourceRegisterType != null;
1903        if (sourceRegisterType.category == RegisterType.Category.Unknown) {
1904            return false;
1905        }
1906
1907        //TODO: check access
1908        //TODO: allow an uninitialized "this" reference, if the current method is an <init> method
1909        Item referencedItem = ((InstructionWithReference)analyzedInstruction.instruction).getReferencedItem();
1910        assert referencedItem instanceof FieldIdItem;
1911        FieldIdItem field = (FieldIdItem)referencedItem;
1912
1913        if (objectRegisterType.category != RegisterType.Category.Null &&
1914                !objectRegisterType.type.extendsClass(ClassPath.getClassDef(field.getContainingClass()))) {
1915            throw new ValidationException(String.format("Cannot access field %s through type %s",
1916                    field.getFieldString(), objectRegisterType.type.getClassType()));
1917        }
1918
1919        RegisterType fieldType = RegisterType.getRegisterTypeForTypeIdItem(field.getFieldType());
1920
1921        if (!WideLowCategories.contains(fieldType.category)) {
1922            throw new ValidationException(String.format("Cannot use %s with field %s. Incorrect field type " +
1923                    "for the instruction.", analyzedInstruction.instruction.opcode.name,
1924                    field.getFieldString()));
1925        }
1926
1927        return true;
1928    }
1929
1930    private static boolean checkArrayFieldAssignment(RegisterType.Category arrayFieldCategory,
1931                                                  RegisterType.Category instructionCategory) {
1932        if (arrayFieldCategory == instructionCategory) {
1933            return true;
1934        }
1935
1936        if ((arrayFieldCategory == RegisterType.Category.Integer &&
1937             instructionCategory == RegisterType.Category.Float) ||
1938            (arrayFieldCategory == RegisterType.Category.Float &&
1939             instructionCategory == RegisterType.Category.Integer)) {
1940            return true;
1941        }
1942        return false;
1943    }
1944
1945    private static void checkRegister(RegisterType registerType, EnumSet validCategories) {
1946        if (!validCategories.contains(registerType.category)) {
1947            //TODO: add expected categories to error message
1948            throw new ValidationException("Invalid register type. Expecting one of: " + " but got \"" +
1949                    registerType.category + "\"");
1950        }
1951    }
1952
1953    private static void checkWideDestinationPair(AnalyzedInstruction analyzedInstruction) {
1954        int register = analyzedInstruction.getDestinationRegister();
1955
1956        if (register == (analyzedInstruction.postRegisterMap.length - 1)) {
1957            throw new ValidationException("v" + register + " is the last register and not a valid wide register " +
1958                    "pair.");
1959        }
1960    }
1961
1962    private static RegisterType getAndCheckWideSourcePair(AnalyzedInstruction analyzedInstruction, int firstRegister) {
1963        assert firstRegister >= 0 && firstRegister < analyzedInstruction.postRegisterMap.length;
1964
1965        if (firstRegister == analyzedInstruction.postRegisterMap.length - 1) {
1966            throw new ValidationException("v" + firstRegister + " is the last register and not a valid wide register " +
1967                    "pair.");
1968        }
1969
1970        RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(firstRegister);
1971        assert registerType != null;
1972        if (registerType.category == RegisterType.Category.Unknown) {
1973            return registerType;
1974        }
1975        checkRegister(registerType, WideLowCategories);
1976
1977        RegisterType secondRegisterType = analyzedInstruction.getPreInstructionRegisterType(firstRegister + 1);
1978        assert secondRegisterType != null;
1979        checkRegister(secondRegisterType, WideHighCategories);
1980
1981        if ((       registerType.category == RegisterType.Category.LongLo &&
1982                    secondRegisterType.category == RegisterType.Category.DoubleHi)
1983            ||  (   registerType.category == RegisterType.Category.DoubleLo &&
1984                    secondRegisterType.category == RegisterType.Category.LongHi)) {
1985            assert false;
1986            throw new ValidationException("The first register in the wide register pair isn't the same type (long " +
1987                    "vs. double) as the second register in the pair");
1988        }
1989
1990        return registerType;
1991    }
1992}
1993