1/*
2 * Copyright 2013, Google Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 *     * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *     * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 *     * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32package org.jf.dexlib2.analysis;
33
34import org.jf.dexlib2.iface.instruction.*;
35import org.jf.dexlib2.iface.reference.MethodReference;
36import org.jf.dexlib2.iface.reference.Reference;
37import org.jf.util.ExceptionWithContext;
38
39import javax.annotation.Nonnull;
40import java.util.*;
41
42public class AnalyzedInstruction implements Comparable<AnalyzedInstruction> {
43    /**
44     * The actual instruction
45     */
46    protected Instruction instruction;
47
48    /**
49     * The index of the instruction, where the first instruction in the method is at index 0, and so on
50     */
51    protected final int instructionIndex;
52
53    /**
54     * Instructions that can pass on execution to this one during normal execution
55     */
56    protected final TreeSet<AnalyzedInstruction> predecessors = new TreeSet<AnalyzedInstruction>();
57
58    /**
59     * Instructions that can execution could pass on to next during normal execution
60     */
61    protected final LinkedList<AnalyzedInstruction> successors = new LinkedList<AnalyzedInstruction>();
62
63    /**
64     * This contains the register types *before* the instruction has executed
65     */
66    protected final RegisterType[] preRegisterMap;
67
68    /**
69     * This contains the register types *after* the instruction has executed
70     */
71    protected final RegisterType[] postRegisterMap;
72
73    /**
74     * When deodexing, we might need to deodex this instruction multiple times, when we merge in new register
75     * information. When this happens, we need to restore the original (odexed) instruction, so we can deodex it again
76     */
77    protected final Instruction originalInstruction;
78
79    public AnalyzedInstruction(Instruction instruction, int instructionIndex, int registerCount) {
80        this.instruction = instruction;
81        this.originalInstruction = instruction;
82        this.instructionIndex = instructionIndex;
83        this.postRegisterMap = new RegisterType[registerCount];
84        this.preRegisterMap = new RegisterType[registerCount];
85        RegisterType unknown = RegisterType.getRegisterType(RegisterType.UNKNOWN, null);
86        for (int i=0; i<registerCount; i++) {
87            preRegisterMap[i] = unknown;
88            postRegisterMap[i] = unknown;
89        }
90    }
91
92    public int getInstructionIndex() {
93        return instructionIndex;
94    }
95
96    public int getPredecessorCount() {
97        return predecessors.size();
98    }
99
100    public SortedSet<AnalyzedInstruction> getPredecessors() {
101        return Collections.unmodifiableSortedSet(predecessors);
102    }
103
104    protected boolean addPredecessor(AnalyzedInstruction predecessor) {
105        return predecessors.add(predecessor);
106    }
107
108    protected void addSuccessor(AnalyzedInstruction successor) {
109        successors.add(successor);
110    }
111
112    protected void setDeodexedInstruction(Instruction instruction) {
113        assert originalInstruction.getOpcode().odexOnly();
114        this.instruction = instruction;
115    }
116
117    protected void restoreOdexedInstruction() {
118        assert originalInstruction.getOpcode().odexOnly();
119        instruction = originalInstruction;
120    }
121
122    public int getSuccessorCount() {
123        return successors.size();
124    }
125
126    public List<AnalyzedInstruction> getSuccesors() {
127        return Collections.unmodifiableList(successors);
128    }
129
130    public Instruction getInstruction() {
131        return instruction;
132    }
133
134    public Instruction getOriginalInstruction() {
135        return originalInstruction;
136    }
137
138    /**
139     * Is this instruction a "beginning instruction". A beginning instruction is defined to be an instruction
140     * that can be the first successfully executed instruction in the method. The first instruction is always a
141     * beginning instruction. If the first instruction can throw an exception, and is covered by a try block, then
142     * the first instruction of any exception handler for that try block is also a beginning instruction. And likewise,
143     * if any of those instructions can throw an exception and are covered by try blocks, the first instruction of the
144     * corresponding exception handler is a beginning instruction, etc.
145     *
146     * To determine this, we simply check if the first predecessor is the fake "StartOfMethod" instruction, which has
147     * an instruction index of -1.
148     * @return a boolean value indicating whether this instruction is a beginning instruction
149     */
150    public boolean isBeginningInstruction() {
151        //if this instruction has no predecessors, it is either the fake "StartOfMethod" instruction or it is an
152        //unreachable instruction.
153        if (predecessors.size() == 0) {
154            return false;
155        }
156
157        if (predecessors.first().instructionIndex == -1) {
158            return true;
159        }
160        return false;
161    }
162
163    /*
164     * Merges the given register type into the specified pre-instruction register, and also sets the post-instruction
165     * register type accordingly if it isn't a destination register for this instruction
166     * @param registerNumber Which register to set
167     * @param registerType The register type
168     * @returns true If the post-instruction register type was changed. This might be false if either the specified
169     * register is a destination register for this instruction, or if the pre-instruction register type didn't change
170     * after merging in the given register type
171     */
172    protected boolean mergeRegister(int registerNumber, RegisterType registerType, BitSet verifiedInstructions) {
173        assert registerNumber >= 0 && registerNumber < postRegisterMap.length;
174        assert registerType != null;
175
176        RegisterType oldRegisterType = preRegisterMap[registerNumber];
177        RegisterType mergedRegisterType = oldRegisterType.merge(registerType);
178
179        if (mergedRegisterType.equals(oldRegisterType)) {
180            return false;
181        }
182
183        preRegisterMap[registerNumber] = mergedRegisterType;
184        verifiedInstructions.clear(instructionIndex);
185
186        if (!setsRegister(registerNumber)) {
187            postRegisterMap[registerNumber] = mergedRegisterType;
188            return true;
189        }
190
191        return false;
192    }
193
194    /**
195     * Iterates over the predecessors of this instruction, and merges all the post-instruction register types for the
196     * given register. Any dead, unreachable, or odexed predecessor is ignored
197     * @param registerNumber the register number
198     * @return The register type resulting from merging the post-instruction register types from all predecessors
199     */
200    protected RegisterType mergePreRegisterTypeFromPredecessors(int registerNumber) {
201        RegisterType mergedRegisterType = null;
202        for (AnalyzedInstruction predecessor: predecessors) {
203            RegisterType predecessorRegisterType = predecessor.postRegisterMap[registerNumber];
204            assert predecessorRegisterType != null;
205            mergedRegisterType = predecessorRegisterType.merge(mergedRegisterType);
206        }
207        return mergedRegisterType;
208    }
209
210    /*
211      * Sets the "post-instruction" register type as indicated.
212      * @param registerNumber Which register to set
213      * @param registerType The "post-instruction" register type
214      * @returns true if the given register type is different than the existing post-instruction register type
215      */
216     protected boolean setPostRegisterType(int registerNumber, RegisterType registerType) {
217         assert registerNumber >= 0 && registerNumber < postRegisterMap.length;
218         assert registerType != null;
219
220         RegisterType oldRegisterType = postRegisterMap[registerNumber];
221         if (oldRegisterType.equals(registerType)) {
222             return false;
223         }
224
225         postRegisterMap[registerNumber] = registerType;
226         return true;
227     }
228
229
230    protected boolean isInvokeInit() {
231        if (instruction == null || !instruction.getOpcode().canInitializeReference()) {
232            return false;
233        }
234
235        ReferenceInstruction instruction = (ReferenceInstruction)this.instruction;
236
237        Reference reference = instruction.getReference();
238        if (reference instanceof MethodReference) {
239            return ((MethodReference)reference).getName().equals("<init>");
240        }
241
242        return false;
243    }
244
245    public boolean setsRegister() {
246        return instruction.getOpcode().setsRegister();
247    }
248
249    public boolean setsWideRegister() {
250        return instruction.getOpcode().setsWideRegister();
251    }
252
253    public boolean setsRegister(int registerNumber) {
254        //When constructing a new object, the register type will be an uninitialized reference after the new-instance
255        //instruction, but becomes an initialized reference once the <init> method is called. So even though invoke
256        //instructions don't normally change any registers, calling an <init> method will change the type of its
257        //object register. If the uninitialized reference has been copied to other registers, they will be initialized
258        //as well, so we need to check for that too
259        if (isInvokeInit()) {
260            int destinationRegister;
261            if (instruction instanceof FiveRegisterInstruction) {
262                destinationRegister = ((FiveRegisterInstruction)instruction).getRegisterC();
263            } else {
264                assert instruction instanceof RegisterRangeInstruction;
265                RegisterRangeInstruction rangeInstruction = (RegisterRangeInstruction)instruction;
266                assert rangeInstruction.getRegisterCount() > 0;
267                destinationRegister = rangeInstruction.getStartRegister();
268            }
269
270            if (registerNumber == destinationRegister) {
271                return true;
272            }
273            RegisterType preInstructionDestRegisterType = getPreInstructionRegisterType(registerNumber);
274            if (preInstructionDestRegisterType.category != RegisterType.UNINIT_REF &&
275                preInstructionDestRegisterType.category != RegisterType.UNINIT_THIS) {
276
277                return false;
278            }
279            //check if the uninit ref has been copied to another register
280            if (getPreInstructionRegisterType(registerNumber).equals(preInstructionDestRegisterType)) {
281                return true;
282            }
283            return false;
284        }
285
286        if (!setsRegister()) {
287            return false;
288        }
289        int destinationRegister = getDestinationRegister();
290
291        if (registerNumber == destinationRegister) {
292            return true;
293        }
294        if (setsWideRegister() && registerNumber == (destinationRegister + 1)) {
295            return true;
296        }
297        return false;
298    }
299
300    public int getDestinationRegister() {
301        if (!this.instruction.getOpcode().setsRegister()) {
302            throw new ExceptionWithContext("Cannot call getDestinationRegister() for an instruction that doesn't " +
303                    "store a value");
304        }
305        return ((OneRegisterInstruction)instruction).getRegisterA();
306    }
307
308    public int getRegisterCount() {
309        return postRegisterMap.length;
310    }
311
312    @Nonnull
313    public RegisterType getPostInstructionRegisterType(int registerNumber) {
314        return postRegisterMap[registerNumber];
315    }
316
317    @Nonnull
318    public RegisterType getPreInstructionRegisterType(int registerNumber) {
319        return preRegisterMap[registerNumber];
320    }
321
322    public int compareTo(AnalyzedInstruction analyzedInstruction) {
323        if (instructionIndex < analyzedInstruction.instructionIndex) {
324            return -1;
325        } else if (instructionIndex == analyzedInstruction.instructionIndex) {
326            return 0;
327        } else {
328            return 1;
329        }
330    }
331}
332
333