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