Home | History | Annotate | Download | only in impl
      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 {
     68     public SmaliMethod(@NotNull SmaliMethodStub stub) {
     69         super(stub, SmaliElementTypes.METHOD);
     70     }
     71 
     72     public SmaliMethod(@NotNull ASTNode node) {
     73         super(node);
     74     }
     75 
     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 
     93     @Override public boolean hasTypeParameters() {
     94         // TODO: (generics) implement this
     95         return false;
     96     }
     97 
     98     @NotNull
     99     public SmaliMethodPrototype getMethodPrototype() {
    100         return getRequiredStubOrPsiChild(SmaliElementTypes.METHOD_PROTOTYPE);
    101     }
    102 
    103     @Nullable @Override public PsiType getReturnType() {
    104         if (isConstructor()) return null;
    105         return getMethodPrototype().getReturnType();
    106     }
    107 
    108     @Nullable @Override public PsiTypeElement getReturnTypeElement() {
    109         if (isConstructor()) return null;
    110         return getMethodPrototype().getReturnTypeElement();
    111     }
    112 
    113     @NotNull @Override public SmaliMethodParamList getParameterList() {
    114         return getMethodPrototype().getParameterList();
    115     }
    116 
    117     @NotNull @Override public SmaliThrowsList getThrowsList() {
    118         return getRequiredStubOrPsiChild(SmaliElementTypes.THROWS_LIST);
    119     }
    120 
    121     @Nullable @Override public PsiCodeBlock getBody() {
    122         // not applicable
    123         return null;
    124     }
    125 
    126     @NotNull public List<SmaliInstruction> getInstructions() {
    127         return findChildrenByType(SmaliElementTypes.INSTRUCTION);
    128     }
    129 
    130     @NotNull public List<SmaliCatchStatement> getCatchStatements() {
    131         return Arrays.asList(findChildrenByClass(SmaliCatchStatement.class));
    132     }
    133 
    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 
    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 
    159     public int getRegisterCount() {
    160         SmaliRegistersStatement registersStatement = findChildByClass(SmaliRegistersStatement.class);
    161         if (registersStatement == null) {
    162             return 0;
    163         }
    164         return registersStatement.getRegisterCount();
    165     }
    166 
    167     public int getParameterRegisterCount() {
    168         int parameterRegisterCount = getMethodPrototype().getParameterList().getParameterRegisterCount();
    169         if (!isStatic()) {
    170             parameterRegisterCount++;
    171         }
    172         return parameterRegisterCount;
    173     }
    174 
    175     @NotNull public SmaliParameterStatement[] getParameterStatements() {
    176         return findChildrenByClass(SmaliParameterStatement.class);
    177     }
    178 
    179     @Override public boolean isConstructor() {
    180         // TODO: should this return true for the class initializer?
    181         return hasModifierProperty("constructor") && !hasModifierProperty("static");
    182     }
    183 
    184     public boolean isStatic() {
    185         return hasModifierProperty("static");
    186     }
    187 
    188     @Override public boolean isVarArgs() {
    189         return hasModifierProperty("varargs");
    190     }
    191 
    192     @NotNull @Override public MethodSignature getSignature(@NotNull PsiSubstitutor substitutor) {
    193         return MethodSignatureBackedByPsiMethod.create(this, substitutor);
    194     }
    195 
    196     @Nullable @Override public SmaliMemberName getNameIdentifier() {
    197         return findChildByClass(SmaliMemberName.class);
    198     }
    199 
    200     @NotNull @Override public PsiMethod[] findSuperMethods() {
    201         return PsiSuperMethodImplUtil.findSuperMethods(this);
    202     }
    203 
    204     @NotNull @Override public PsiMethod[] findSuperMethods(boolean checkAccess) {
    205         return PsiSuperMethodImplUtil.findSuperMethods(this, checkAccess);
    206     }
    207 
    208     @NotNull @Override public PsiMethod[] findSuperMethods(PsiClass parentClass) {
    209         return PsiSuperMethodImplUtil.findSuperMethods(this, parentClass);
    210     }
    211 
    212     @NotNull @Override
    213     public List<MethodSignatureBackedByPsiMethod> findSuperMethodSignaturesIncludingStatic(boolean checkAccess) {
    214         return PsiSuperMethodImplUtil.findSuperMethodSignaturesIncludingStatic(this, checkAccess);
    215     }
    216 
    217     @Nullable @Override public PsiMethod findDeepestSuperMethod() {
    218         return PsiSuperMethodImplUtil.findDeepestSuperMethod(this);
    219     }
    220 
    221     @NotNull @Override public PsiMethod[] findDeepestSuperMethods() {
    222         return PsiSuperMethodImplUtil.findDeepestSuperMethods(this);
    223     }
    224 
    225     @NotNull @Override public SmaliModifierList getModifierList() {
    226         return getRequiredStubOrPsiChild(SmaliElementTypes.MODIFIER_LIST);
    227     }
    228 
    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 
    238     @NotNull @Override public HierarchicalMethodSignature getHierarchicalMethodSignature() {
    239         return PsiSuperMethodImplUtil.getHierarchicalMethodSignature(this);
    240     }
    241 
    242     @Nullable @Override public PsiDocComment getDocComment() {
    243         // not applicable
    244         return null;
    245     }
    246 
    247     @Override public boolean isDeprecated() {
    248         return PsiImplUtil.isDeprecatedByAnnotation(this);
    249     }
    250 
    251     @Nullable @Override public PsiTypeParameterList getTypeParameterList() {
    252         // TODO: (generics) implement this
    253         return null;
    254     }
    255 
    256     @NotNull @Override public PsiTypeParameter[] getTypeParameters() {
    257         // TODO: (generics) implement this
    258         return new PsiTypeParameter[0];
    259     }
    260 
    261     @Nullable @Override public SmaliClass getContainingClass() {
    262         PsiElement parent = getStubOrPsiParent();
    263         if (parent instanceof SmaliClass) {
    264             return (SmaliClass) parent;
    265         }
    266         return null;
    267     }
    268 
    269     @Override public boolean hasModifierProperty(@ModifierConstant @NonNls @NotNull String name) {
    270         return getModifierList().hasModifierProperty(name);
    271     }
    272 
    273     @NotNull @Override public SmaliAnnotation[] getAnnotations() {
    274         return getStubOrPsiChildren(SmaliElementTypes.ANNOTATION, new SmaliAnnotation[0]);
    275     }
    276 
    277     @NotNull @Override public SmaliAnnotation[] getApplicableAnnotations() {
    278         return getAnnotations();
    279     }
    280 
    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 
    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 
    308     @Nullable public SmaliLabel getLabel(String name) {
    309         return labelMap.get().get(name);
    310     }
    311 
    312     private MethodAnalyzer methodAnalyzer = null;
    313 
    314     @Nullable
    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 
    336     @Override public void subtreeChanged() {
    337         super.subtreeChanged();
    338         methodAnalyzer = null;
    339     }
    340 
    341     @Override public int getTextOffset() {
    342         SmaliMemberName smaliMemberName = getNameIdentifier();
    343         if (smaliMemberName != null) {
    344             return smaliMemberName.getTextOffset();
    345         }
    346         return super.getTextOffset();
    347     }
    348 
    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