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