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