SmaliMethod.java revision 16dde79e587da4ea840293e196a02101b1aafc22
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
32package org.jf.smalidea.psi.impl;
33
34import com.google.common.base.Supplier;
35import com.google.common.base.Suppliers;
36import com.google.common.collect.Maps;
37import com.intellij.debugger.SourcePosition;
38import com.intellij.lang.ASTNode;
39import com.intellij.openapi.editor.Document;
40import com.intellij.psi.*;
41import com.intellij.psi.PsiModifier.ModifierConstant;
42import com.intellij.psi.impl.PsiImplUtil;
43import com.intellij.psi.impl.PsiSuperMethodImplUtil;
44import com.intellij.psi.javadoc.PsiDocComment;
45import com.intellij.psi.util.MethodSignature;
46import com.intellij.psi.util.MethodSignatureBackedByPsiMethod;
47import com.intellij.util.IncorrectOperationException;
48import org.jetbrains.annotations.NonNls;
49import org.jetbrains.annotations.NotNull;
50import org.jetbrains.annotations.Nullable;
51import org.jf.dexlib2.analysis.ClassPath;
52import org.jf.dexlib2.analysis.MethodAnalyzer;
53import org.jf.smalidea.dexlib.SmalideaMethod;
54import org.jf.smalidea.psi.SmaliElementTypes;
55import org.jf.smalidea.psi.iface.SmaliModifierListOwner;
56import org.jf.smalidea.psi.stub.SmaliMethodStub;
57
58import java.io.IOException;
59import java.util.Arrays;
60import java.util.List;
61import java.util.Map;
62
63public class SmaliMethod extends SmaliStubBasedPsiElement<SmaliMethodStub>
64        implements PsiMethod, SmaliModifierListOwner {
65    public SmaliMethod(@NotNull SmaliMethodStub stub) {
66        super(stub, SmaliElementTypes.METHOD);
67    }
68
69    public SmaliMethod(@NotNull ASTNode node) {
70        super(node);
71    }
72
73    @NotNull @Override public String getName() {
74        SmaliMethodStub stub = getStub();
75        String name = null;
76        if (stub != null) {
77            name = stub.getName();
78        } else {
79            SmaliMemberName nameIdentifier = getNameIdentifier();
80            if (nameIdentifier != null) {
81                name = nameIdentifier.getText();
82            }
83        }
84        if (name == null || name.isEmpty()) {
85            name = "<unnamed>";
86        }
87        return name;
88    }
89
90    @Override public boolean hasTypeParameters() {
91        // TODO: (generics) implement this
92        return false;
93    }
94
95    @NotNull
96    public SmaliMethodPrototype getMethodPrototype() {
97        return getRequiredStubOrPsiChild(SmaliElementTypes.METHOD_PROTOTYPE);
98    }
99
100    @Nullable @Override public PsiType getReturnType() {
101        SmaliMethodStub stub = getStub();
102        if (stub != null) {
103            String returnType = stub.getReturnType();
104            if (returnType == null) {
105                return null;
106            }
107            PsiElementFactory factory = JavaPsiFacade.getInstance(getProject()).getElementFactory();
108            return factory.createTypeByFQClassName(returnType, getResolveScope());
109        }
110        PsiTypeElement returnTypeElement = getReturnTypeElement();
111        if (returnTypeElement == null) {
112            return null;
113        }
114        return returnTypeElement.getType();
115    }
116
117    @Nullable @Override public PsiTypeElement getReturnTypeElement() {
118        return getMethodPrototype().getReturnType();
119    }
120
121    @NotNull @Override public SmaliMethodParamList getParameterList() {
122        return getMethodPrototype().getParameterList();
123    }
124
125    @NotNull @Override public PsiReferenceList getThrowsList() {
126        // TODO: add a fake reference list for throws
127        return null;
128    }
129
130    @Nullable @Override public PsiCodeBlock getBody() {
131        // not applicable
132        return null;
133    }
134
135    @NotNull public List<SmaliInstruction> getInstructions() {
136        return findChildrenByType(SmaliElementTypes.INSTRUCTION);
137    }
138
139    @NotNull public List<SmaliCatchStatement> getCatchStatements() {
140        return Arrays.asList(findChildrenByClass(SmaliCatchStatement.class));
141    }
142
143    @Nullable public SourcePosition getSourcePositionForCodeOffset(int offset) {
144        for (SmaliInstruction instruction: getInstructions()) {
145            if (instruction.getOffset() >= offset) {
146                return SourcePosition.createFromElement(instruction);
147            }
148        }
149        return null;
150    }
151
152    public int getOffsetForLine(int line) {
153        PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject());
154        final Document document = documentManager.getDocument(getContainingFile());
155        if (document == null) {
156            return -1;
157        }
158
159        for (final SmaliInstruction instruction: getInstructions()) {
160            int curLine = document.getLineNumber(instruction.getTextOffset());
161            if (curLine >= line) {
162                return instruction.getOffset();
163            }
164        }
165        return -1;
166    }
167
168    public int getRegisterCount() {
169        SmaliRegistersStatement registersStatement = findChildByClass(SmaliRegistersStatement.class);
170        if (registersStatement == null) {
171            return 0;
172        }
173        return registersStatement.getRegisterCount();
174    }
175
176    public int getParameterRegisterCount() {
177        int parameterRegisterCount = getMethodPrototype().getParameterList().getParameterRegisterCount();
178        if (!isStatic()) {
179            parameterRegisterCount++;
180        }
181        return parameterRegisterCount;
182    }
183
184    @NotNull public SmaliParameterStatement[] getParameterStatements() {
185        return findChildrenByClass(SmaliParameterStatement.class);
186    }
187
188    @Override public boolean isConstructor() {
189        return hasModifierProperty("constructor") && !hasModifierProperty("static");
190    }
191
192    public boolean isStatic() {
193        return hasModifierProperty("static");
194    }
195
196    @Override public boolean isVarArgs() {
197        return hasModifierProperty("varargs");
198    }
199
200    @NotNull @Override public MethodSignature getSignature(@NotNull PsiSubstitutor substitutor) {
201        return MethodSignatureBackedByPsiMethod.create(this, substitutor);
202    }
203
204    @Nullable @Override public SmaliMemberName getNameIdentifier() {
205        return findChildByClass(SmaliMemberName.class);
206    }
207
208    @NotNull @Override public PsiMethod[] findSuperMethods() {
209        return PsiSuperMethodImplUtil.findSuperMethods(this);
210    }
211
212    @NotNull @Override public PsiMethod[] findSuperMethods(boolean checkAccess) {
213        return PsiSuperMethodImplUtil.findSuperMethods(this, checkAccess);
214    }
215
216    @NotNull @Override public PsiMethod[] findSuperMethods(PsiClass parentClass) {
217        return PsiSuperMethodImplUtil.findSuperMethods(this, parentClass);
218    }
219
220    @NotNull @Override
221    public List<MethodSignatureBackedByPsiMethod> findSuperMethodSignaturesIncludingStatic(boolean checkAccess) {
222        return PsiSuperMethodImplUtil.findSuperMethodSignaturesIncludingStatic(this, checkAccess);
223    }
224
225    @Nullable @Override public PsiMethod findDeepestSuperMethod() {
226        return PsiSuperMethodImplUtil.findDeepestSuperMethod(this);
227    }
228
229    @NotNull @Override public PsiMethod[] findDeepestSuperMethods() {
230        return PsiSuperMethodImplUtil.findDeepestSuperMethods(this);
231    }
232
233    @NotNull @Override public SmaliModifierList getModifierList() {
234        return getRequiredStubOrPsiChild(SmaliElementTypes.MODIFIER_LIST);
235    }
236
237    @Override public PsiElement setName(@NonNls @NotNull String name) throws IncorrectOperationException {
238        // TODO: implement this
239        throw new IncorrectOperationException();
240    }
241
242    @NotNull @Override public HierarchicalMethodSignature getHierarchicalMethodSignature() {
243        return PsiSuperMethodImplUtil.getHierarchicalMethodSignature(this);
244    }
245
246    @Nullable @Override public PsiDocComment getDocComment() {
247        // not applicable
248        return null;
249    }
250
251    @Override public boolean isDeprecated() {
252        return PsiImplUtil.isDeprecatedByAnnotation(this);
253    }
254
255    @Nullable @Override public PsiTypeParameterList getTypeParameterList() {
256        // TODO: (generics) implement this
257        return null;
258    }
259
260    @NotNull @Override public PsiTypeParameter[] getTypeParameters() {
261        // TODO: (generics) implement this
262        return new PsiTypeParameter[0];
263    }
264
265    @Nullable @Override public PsiClass getContainingClass() {
266        return (SmaliClass)getStubOrPsiParent();
267    }
268
269    @Override public boolean hasModifierProperty(@ModifierConstant @NonNls @NotNull String name) {
270        return getModifierList().hasModifierProperty(name);
271    }
272
273    @Nullable @Override public SmaliAccessList getAccessFlagsNode() {
274        return findChildByClass(SmaliAccessList.class);
275    }
276
277    @NotNull @Override public SmaliAnnotation[] getAnnotations() {
278        return getStubOrPsiChildren(SmaliElementTypes.ANNOTATION, new SmaliAnnotation[0]);
279    }
280
281    @NotNull @Override public SmaliAnnotation[] getApplicableAnnotations() {
282        return getAnnotations();
283    }
284
285    @Nullable @Override public SmaliAnnotation findAnnotation(@NotNull @NonNls String qualifiedName) {
286        for (SmaliAnnotation annotation: getAnnotations()) {
287            if (qualifiedName.equals(annotation.getQualifiedName())) {
288                return annotation;
289            }
290        }
291        return null;
292    }
293
294    @NotNull @Override public SmaliAnnotation addAnnotation(@NotNull @NonNls String qualifiedName) {
295        // TODO: implement this
296        return null;
297    }
298
299    private final Supplier<Map<String, SmaliLabel>> labelMap = Suppliers.memoize(
300            new Supplier<Map<String, SmaliLabel>>() {
301                @Override public Map<String, SmaliLabel> get() {
302                    Map<String, SmaliLabel> labelMap = Maps.newHashMap();
303                    for (SmaliLabel label: findChildrenByClass(SmaliLabel.class)) {
304                        if (!labelMap.containsKey(label.getText())) {
305                            labelMap.put(label.getText(), label);
306                        }
307                    }
308                    return labelMap;
309                }
310            });
311
312    @Nullable public SmaliLabel getLabel(String name) {
313        return labelMap.get().get(name);
314    }
315
316    private MethodAnalyzer methodAnalyzer = null;
317
318    @NotNull
319    public MethodAnalyzer getMethodAnalyzer() {
320        if (methodAnalyzer == null) {
321            ClassPath classPath;
322            try {
323                classPath = new ClassPath();
324            } catch (IOException ex) {
325                throw new RuntimeException(ex);
326            }
327
328            methodAnalyzer = new MethodAnalyzer(classPath, new SmalideaMethod(SmaliMethod.this), null);
329        }
330        return methodAnalyzer;
331    }
332
333    @Override public void subtreeChanged() {
334        super.subtreeChanged();
335        methodAnalyzer = null;
336    }
337}
338