• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2014, 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 
32 package org.jf.smalidea.psi.impl;
33 
34 import com.google.common.base.Preconditions;
35 import com.intellij.lang.ASTNode;
36 import org.jetbrains.annotations.NotNull;
37 import org.jetbrains.annotations.Nullable;
38 import org.jf.dexlib2.Format;
39 import org.jf.dexlib2.Opcode;
40 import org.jf.dexlib2.Opcodes;
41 import org.jf.dexlib2.analysis.AnalyzedInstruction;
42 import org.jf.dexlib2.analysis.MethodAnalyzer;
43 import org.jf.smalidea.SmaliTokens;
44 import org.jf.smalidea.psi.SmaliCompositeElementFactory;
45 import org.jf.smalidea.psi.SmaliElementTypes;
46 
47 import java.util.Arrays;
48 import java.util.List;
49 
50 public class SmaliInstruction extends SmaliCompositeElement {
51     private static final int NO_OFFSET = -1;
52 
53     @Nullable private Opcode opcode;
54     private int offset = NO_OFFSET;
55 
56     public static final SmaliCompositeElementFactory FACTORY = new SmaliCompositeElementFactory() {
57         @Override public SmaliCompositeElement createElement() {
58             return new SmaliInstruction();
59         }
60     };
61 
SmaliInstruction()62     public SmaliInstruction() {
63         super(SmaliElementTypes.INSTRUCTION);
64     }
65 
getParentMethod()66     @NotNull public SmaliMethod getParentMethod() {
67         SmaliMethod smaliMethod = findAncestorByClass(SmaliMethod.class);
68         assert smaliMethod != null;
69         return smaliMethod;
70     }
71 
getOpcode()72     @NotNull public Opcode getOpcode() {
73         if (opcode == null) {
74             ASTNode instructionNode = findChildByType(SmaliTokens.INSTRUCTION_TOKENS);
75             // this should be impossible, based on the parser definition
76             assert instructionNode != null;
77 
78             // TODO: put a project level Opcodes instance with the appropriate api level somewhere
79             opcode = Opcodes.getDefault().getOpcodeByName(instructionNode.getText());
80             if (opcode == null) {
81                 if (instructionNode.getText().equals(".packed-switch")) {
82                     return Opcode.PACKED_SWITCH_PAYLOAD;
83                 }
84                 if (instructionNode.getText().equals(".sparse-switch")) {
85                     return Opcode.SPARSE_SWITCH_PAYLOAD;
86                 }
87                 if (instructionNode.getText().equals(".array-data")) {
88                     return Opcode.ARRAY_PAYLOAD;
89                 }
90                 assert false;
91             }
92         }
93         return opcode;
94     }
95 
getOffset()96     public int getOffset() {
97         // TODO: don't calculate this recursively. ugh!
98         if (offset == NO_OFFSET) {
99             SmaliInstruction previousInstruction = findPrevSiblingByClass(SmaliInstruction.class);
100             if (previousInstruction == null) {
101                 offset = 0;
102             } else {
103                 offset = previousInstruction.getOffset() + previousInstruction.getInstructionSize();
104             }
105         }
106         return offset;
107     }
108 
getRegister(int registerIndex)109     public int getRegister(int registerIndex) {
110         Preconditions.checkArgument(registerIndex >= 0);
111 
112         List<ASTNode> registers = findChildrenByType(SmaliElementTypes.REGISTER_REFERENCE);
113         if (registerIndex >= registers.size()) {
114             return -1;
115         }
116 
117         SmaliRegisterReference registerReference = (SmaliRegisterReference)registers.get(registerIndex);
118         return registerReference.getRegisterNumber();
119     }
120 
121     @Nullable
getTarget()122     public SmaliLabelReference getTarget() {
123         return findChildByClass(SmaliLabelReference.class);
124     }
125 
getRegisterCount()126     public int getRegisterCount() {
127         return findChildrenByType(SmaliElementTypes.REGISTER_REFERENCE).size();
128     }
129 
130     @Nullable
getLiteral()131     public SmaliLiteral getLiteral() {
132         return findChildByClass(SmaliLiteral.class);
133     }
134 
135     @Nullable
getTypeReference()136     public SmaliTypeElement getTypeReference() {
137         return findChildByClass(SmaliTypeElement.class);
138     }
139 
140     @Nullable
getFieldReference()141     public SmaliFieldReference getFieldReference() {
142         return findChildByClass(SmaliFieldReference.class);
143     }
144 
145     @Nullable
getMethodReference()146     public SmaliMethodReference getMethodReference() {
147         return findChildByClass(SmaliMethodReference.class);
148     }
149 
150     @Nullable
getPackedSwitchStartKey()151     public SmaliLiteral getPackedSwitchStartKey() {
152         return findChildByClass(SmaliLiteral.class);
153     }
154 
155     @NotNull
getPackedSwitchElements()156     public List<SmaliPackedSwitchElement> getPackedSwitchElements() {
157         return Arrays.asList(findChildrenByClass(SmaliPackedSwitchElement.class));
158     }
159 
160     @NotNull
getSparseSwitchElements()161     public List<SmaliSparseSwitchElement> getSparseSwitchElements() {
162         return Arrays.asList(findChildrenByClass(SmaliSparseSwitchElement.class));
163     }
164 
165     @Nullable
getArrayDataWidth()166     public SmaliLiteral getArrayDataWidth() {
167         return findChildByClass(SmaliLiteral.class);
168     }
169 
170     @NotNull
getArrayDataElements()171     public List<SmaliArrayDataElement> getArrayDataElements() {
172         return Arrays.asList(findChildrenByClass(SmaliArrayDataElement.class));
173     }
174 
getInstructionSize()175     public int getInstructionSize() {
176         Opcode opcode = getOpcode();
177         if (!opcode.format.isPayloadFormat) {
178             return opcode.format.size;
179         } else if (opcode.format == Format.ArrayPayload) {
180             int elementWidth = (int)getArrayDataWidth().getIntegralValue();
181             int elementCount = getArrayDataElements().size();
182 
183             return 8 + (elementWidth * elementCount + 1);
184         } else if (opcode.format == Format.PackedSwitchPayload) {
185             return 8 + getPackedSwitchElements().size() * 4;
186         } else if (opcode.format == Format.SparseSwitchPayload) {
187             return 2 + getSparseSwitchElements().size() * 4;
188         }
189         assert false;
190         throw new RuntimeException();
191     }
192 
193     private AnalyzedInstruction analyzedInstruction = null;
194 
195     @Nullable
getAnalyzedInstructionFromMethod()196     private AnalyzedInstruction getAnalyzedInstructionFromMethod() {
197         SmaliMethod method = getParentMethod();
198 
199         MethodAnalyzer analyzer = method.getMethodAnalyzer();
200         if (analyzer == null) {
201             return null;
202         }
203 
204         int thisOffset = this.getOffset() / 2;
205         int codeOffset = 0;
206 
207         for (AnalyzedInstruction instruction: analyzer.getAnalyzedInstructions()) {
208             if (codeOffset == thisOffset) {
209                 return instruction;
210             }
211             assert codeOffset < thisOffset;
212 
213             codeOffset += instruction.getOriginalInstruction().getCodeUnits();
214         }
215         assert false;
216         return null;
217     }
218 
219     @Nullable
220     public AnalyzedInstruction getAnalyzedInstruction() {
221         if (analyzedInstruction == null) {
222             analyzedInstruction = getAnalyzedInstructionFromMethod();
223         }
224         return analyzedInstruction;
225     }
226 
227     @Override public void clearCaches() {
228         super.clearCaches();
229         analyzedInstruction = null;
230     }
231 }
232