AnalyzedInstruction.java revision c9be5e13034da9827b5598a6257376164745b827
1package org.jf.dexlib.Code.Analysis;
2
3import org.jf.dexlib.Code.*;
4import org.jf.dexlib.Item;
5import org.jf.dexlib.ItemType;
6import org.jf.dexlib.MethodIdItem;
7import org.jf.dexlib.Util.ExceptionWithContext;
8
9import java.util.LinkedList;
10
11public class AnalyzedInstruction {
12    /**
13     * The actual instruction
14     */
15    public final Instruction instruction;
16
17    /**
18     * The index of the instruction, where the first instruction in the method is at index 0, and so on
19     */
20    protected final int instructionIndex;
21
22    /**
23     * Instructions that can pass on execution to this one during normal execution
24     */
25    protected final LinkedList<AnalyzedInstruction> predecessors = new LinkedList<AnalyzedInstruction>();
26
27    /**
28     * Instructions that can execution could pass on to next during normal execution
29     */
30    protected final LinkedList<AnalyzedInstruction> successors = new LinkedList<AnalyzedInstruction>();
31
32    /**
33     * This contains the register types *after* the instruction has executed
34     */
35    protected final RegisterType[] postRegisterMap;
36
37    /**
38     * This is set to true when this instruction follows an odexed instruction that couldn't be deodexed. In this case
39     * the unodexable instruction is guaranteed to throw an NPE, so anything following it is dead, up until a non-dead
40     * code path merges in. And more importantly, the code following the unodexable instruction isn't verifiable in
41     * some cases, if it depends on the return/field type of the unodexeable instruction. Meaning that if the "dead"
42     * code was left in, dalvik would reject it because it couldn't verify the register types. In some cases, this
43     * dead code could be left in without ill-effect, but it's easier to always remove it, which is always valid. Since
44     * it is dead code, removing it won't have any effect.
45     */
46    protected boolean dead = false;
47
48    public AnalyzedInstruction(Instruction instruction, int instructionIndex, int registerCount) {
49        this.instruction = instruction;
50        this.instructionIndex = instructionIndex;
51        this.postRegisterMap = new RegisterType[registerCount];
52        RegisterType unknown = RegisterType.getRegisterType(RegisterType.Category.Unknown, null);
53        for (int i=0; i<registerCount; i++) {
54            postRegisterMap[i] = unknown;
55        }
56    }
57
58    public int getInstructionIndex() {
59        return instructionIndex;
60    }
61
62    public int getPredecessorCount() {
63        return predecessors.size();
64    }
65
66    protected void addPredecessor(AnalyzedInstruction predecessor) {
67        predecessors.add(predecessor);
68    }
69
70    /**
71     * @return true if the successor was added or false if it wasn't added because it already existed
72     */
73    protected boolean addSuccessor(AnalyzedInstruction successor) {
74        for (AnalyzedInstruction instruction: successors) {
75            if (instruction == successor) {
76                return false;
77            }
78        }
79        successors.add(successor);
80        return true;
81    }
82
83    /*
84     * Sets the "post-instruction" register type as indicated. This should only be used to set
85     * the method parameter types for the "start of method" instruction, or to set the register
86     * type of the destination register during verification. The change to the register type
87     * will
88     * @param registerNumber Which register to set
89     * @param registerType The "post-instruction" register type
90     */
91    protected boolean setPostRegisterType(int registerNumber, RegisterType registerType) {
92        assert registerNumber >= 0 && registerNumber < postRegisterMap.length;
93        assert registerType != null;
94
95        RegisterType oldRegisterType = postRegisterMap[registerNumber];
96        if (oldRegisterType == registerType) {
97            return false;
98        }
99
100        postRegisterMap[registerNumber] = registerType;
101        return true;
102    }
103
104    protected RegisterType getMergedRegisterTypeFromPredecessors(int registerNumber) {
105        RegisterType mergedRegisterType = null;
106        for (AnalyzedInstruction predecessor: predecessors) {
107            RegisterType predecessorRegisterType = predecessor.postRegisterMap[registerNumber];
108            assert predecessorRegisterType != null;
109            mergedRegisterType = predecessorRegisterType.merge(mergedRegisterType);
110        }
111        return mergedRegisterType;
112    }
113
114    protected boolean isInvokeInit() {
115        if (instruction == null ||
116                (instruction.opcode != Opcode.INVOKE_DIRECT && instruction.opcode != Opcode.INVOKE_DIRECT_RANGE)) {
117            return false;
118        }
119
120        //TODO: check access flags instead of name?
121
122        InstructionWithReference instruction = (InstructionWithReference)this.instruction;
123        Item item = instruction.getReferencedItem();
124        assert item.getItemType() == ItemType.TYPE_METHOD_ID_ITEM;
125        MethodIdItem method = (MethodIdItem)item;
126
127        if (!method.getMethodName().getStringValue().equals("<init>")) {
128            return false;
129        }
130
131        return true;
132    }
133
134    public boolean setsRegister() {
135        return instruction.opcode.setsRegister();
136    }
137
138    public boolean setsWideRegister() {
139        return instruction.opcode.setsWideRegister();
140    }
141
142    public boolean setsRegister(int registerNumber) {
143
144        //When constructing a new object, the register type will be an uninitialized reference after the new-instance
145        //instruction, but becomes an initialized reference once the <init> method is called. So even though invoke
146        //instructions don't normally change any registers, calling an <init> method will change the type of its
147        //object register. If the uninitialized reference has been copied to other registers, they will be initialized
148        //as well, so we need to check for that too
149        if (isInvokeInit()) {
150            int destinationRegister;
151            if (instruction instanceof FiveRegisterInstruction) {
152                destinationRegister = ((FiveRegisterInstruction)instruction).getRegisterD();
153            } else {
154                assert instruction instanceof RegisterRangeInstruction;
155                RegisterRangeInstruction rangeInstruction = (RegisterRangeInstruction)instruction;
156                assert rangeInstruction.getRegCount() > 0;
157                destinationRegister = rangeInstruction.getStartRegister();
158            }
159
160            if (registerNumber == destinationRegister) {
161                return true;
162            }
163            RegisterType preInstructionDestRegisterType = getMergedRegisterTypeFromPredecessors(registerNumber);
164            if (preInstructionDestRegisterType.category != RegisterType.Category.UninitRef &&
165                preInstructionDestRegisterType.category != RegisterType.Category.UninitThis) {
166
167                return false;
168            }
169            //check if the uninit ref has been copied to another register
170            if (getMergedRegisterTypeFromPredecessors(registerNumber) == preInstructionDestRegisterType) {
171                return true;
172            }
173            return false;
174        }
175
176        if (!setsRegister()) {
177            return false;
178        }
179        int destinationRegister = getDestinationRegister();
180
181        if (registerNumber == destinationRegister) {
182            return true;
183        }
184        if (setsWideRegister() && registerNumber == (destinationRegister + 1)) {
185            return true;
186        }
187        return false;
188    }
189
190    public int getDestinationRegister() {
191        if (!this.instruction.opcode.setsRegister()) {
192            throw new ExceptionWithContext("Cannot call getDestinationRegister() for an instruction that doesn't " +
193                    "store a value");
194        }
195        return ((SingleRegisterInstruction)instruction).getRegisterA();
196    }
197
198    public int getRegisterCount() {
199        return postRegisterMap.length;
200    }
201
202    public RegisterType getPostInstructionRegisterType(int registerNumber) {
203        return postRegisterMap[registerNumber];
204    }
205
206    public RegisterType getPreInstructionRegisterType(int registerNumber) {
207        //if the specific register is not a destination register, then the stored post-instruction register type will
208        //be the same as the pre-instruction regsiter type, so we can use that.
209        //otherwise, we need to merge the predecessor's post-instruction register types
210
211        if (this.setsRegister(registerNumber)) {
212            return getMergedRegisterTypeFromPredecessors(registerNumber);
213        } else {
214            return postRegisterMap[registerNumber];
215        }
216    }
217}
218
219