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