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