1 /* 2 * Copyright 2015, 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.findUsages; 33 34 import com.intellij.psi.*; 35 import com.intellij.psi.tree.IElementType; 36 import com.intellij.usages.impl.rules.UsageType; 37 import com.intellij.usages.impl.rules.UsageTypeProvider; 38 import org.jetbrains.annotations.NotNull; 39 import org.jetbrains.annotations.Nullable; 40 import org.jf.dexlib2.Opcode; 41 import org.jf.smalidea.SmaliTokens; 42 import org.jf.smalidea.psi.impl.*; 43 44 import java.util.EnumSet; 45 import java.util.Set; 46 47 public class SmaliUsageTypeProvider implements UsageTypeProvider { 48 49 static final UsageType CLASS_DECLARATION = new UsageType("Class declaration"); 50 static final UsageType VERIFICATION_ERROR = new UsageType("Usage in verification error"); 51 static final UsageType FIELD_TYPE_REFERENCE = new UsageType("Usage as field type in a field reference"); 52 static final UsageType FIELD_DECLARING_TYPE_REFERENCE = new UsageType("Usage as a declaring type in a field reference"); 53 static final UsageType METHOD_RETURN_TYPE_REFERENCE = new UsageType("Usage as return type in a method reference"); 54 static final UsageType METHOD_PARAM_REFERENCE = new UsageType("Usage as parameter in a method reference"); 55 static final UsageType METHOD_DECLARING_TYPE_REFERENCE = new UsageType("Usage as a declaring type in a method reference"); 56 static final UsageType LITERAL = new UsageType("Usage as a literal"); 57 getUsageType(PsiElement element)58 @Nullable @Override public UsageType getUsageType(PsiElement element) { 59 if (element instanceof PsiReference) { 60 PsiElement referenced = ((PsiReference) element).resolve(); 61 if (referenced != null) { 62 if (referenced instanceof PsiClass) { 63 return findClassUsageType(element); 64 } else if (referenced instanceof PsiField) { 65 return findFieldUsageType(element); 66 } else if (referenced instanceof PsiMethod) { 67 return findMethodUsageType(element); 68 } 69 } 70 } 71 return UsageType.UNCLASSIFIED; 72 } 73 74 private final Set<Opcode> newArrayInstructions = EnumSet.of(Opcode.FILLED_NEW_ARRAY, Opcode.NEW_ARRAY, 75 Opcode.FILLED_NEW_ARRAY_RANGE); 76 77 private final Set<Opcode> fieldReadInstructions = EnumSet.of(Opcode.IGET, Opcode.IGET_BOOLEAN, Opcode.IGET_BYTE, 78 Opcode.IGET_CHAR, Opcode.IGET_OBJECT, Opcode.IGET_OBJECT_VOLATILE, Opcode.IGET_SHORT, Opcode.IGET_VOLATILE, 79 Opcode.IGET_WIDE, Opcode.IGET_WIDE_VOLATILE, Opcode.SGET, Opcode.SGET_BOOLEAN, Opcode.SGET_BYTE, 80 Opcode.SGET_CHAR, Opcode.SGET_OBJECT, Opcode.SGET_OBJECT_VOLATILE, Opcode.SGET_SHORT, Opcode.SGET_VOLATILE, 81 Opcode.SGET_WIDE, Opcode.SGET_WIDE_VOLATILE); 82 83 private final Set<Opcode> fieldWriteInstructions = EnumSet.of(Opcode.IPUT, Opcode.IPUT_BOOLEAN, Opcode.IPUT_BYTE, 84 Opcode.IPUT_CHAR, Opcode.IPUT_OBJECT, Opcode.IPUT_OBJECT_VOLATILE, Opcode.IPUT_SHORT, Opcode.IPUT_VOLATILE, 85 Opcode.IPUT_WIDE, Opcode.IPUT_WIDE_VOLATILE, Opcode.SPUT, Opcode.SPUT_BOOLEAN, Opcode.SPUT_BYTE, 86 Opcode.SPUT_CHAR, Opcode.SPUT_OBJECT, Opcode.SPUT_OBJECT_VOLATILE, Opcode.SPUT_SHORT, Opcode.SPUT_VOLATILE, 87 Opcode.SPUT_WIDE, Opcode.SPUT_WIDE_VOLATILE); 88 89 @Nullable findClassUsageType(@otNull PsiElement element)90 private UsageType findClassUsageType(@NotNull PsiElement element) { 91 PsiElement originalElement = element; 92 93 while (element != null) { 94 if (element instanceof SmaliFieldReference) { 95 PsiElement prev = originalElement.getPrevSibling(); 96 while (prev != null) { 97 // if the element is to the right of a colon, then it is the field type, otherwise it is 98 // the declaring class 99 if (prev.getNode().getElementType() == SmaliTokens.COLON) { 100 return FIELD_TYPE_REFERENCE; 101 } 102 prev = prev.getPrevSibling(); 103 } 104 return FIELD_DECLARING_TYPE_REFERENCE; 105 } else if (element instanceof SmaliMethodReferenceParamList) { 106 return METHOD_PARAM_REFERENCE; 107 } else if (element instanceof SmaliMethodReference) { 108 PsiElement prev = originalElement.getPrevSibling(); 109 while (prev != null) { 110 IElementType elementType = prev.getNode().getElementType(); 111 // if the element is to the right of a close paren, then it is the return type, 112 // otherwise it is the declaring class. Any parameter type will be taken care of by the previous 113 // "if" for SmaliMethodReferenceParamList 114 if (elementType == SmaliTokens.CLOSE_PAREN) { 115 return METHOD_RETURN_TYPE_REFERENCE; 116 } 117 prev = prev.getPrevSibling(); 118 } 119 return METHOD_DECLARING_TYPE_REFERENCE; 120 } else if (element instanceof SmaliInstruction) { 121 Opcode opcode = ((SmaliInstruction) element).getOpcode(); 122 if (opcode == Opcode.INSTANCE_OF) { 123 return UsageType.CLASS_INSTANCE_OF; 124 } else if (opcode == Opcode.CHECK_CAST) { 125 return UsageType.CLASS_CAST_TO; 126 } else if (newArrayInstructions.contains(opcode)) { 127 return UsageType.CLASS_NEW_ARRAY; 128 } else if (opcode == Opcode.NEW_INSTANCE) { 129 return UsageType.CLASS_NEW_OPERATOR; 130 } else if (opcode == Opcode.CONST_CLASS) { 131 return UsageType.CLASS_CLASS_OBJECT_ACCESS; 132 } else if (opcode == Opcode.THROW_VERIFICATION_ERROR) { 133 return VERIFICATION_ERROR; 134 } 135 } else if (element instanceof SmaliSuperStatement || element instanceof SmaliImplementsStatement) { 136 return UsageType.CLASS_EXTENDS_IMPLEMENTS_LIST; 137 } else if (element instanceof SmaliClassStatement) { 138 return CLASS_DECLARATION; 139 } else if (element instanceof SmaliMethodParamList) { 140 return UsageType.CLASS_METHOD_PARAMETER_DECLARATION; 141 } else if (element instanceof SmaliMethodPrototype) { 142 return UsageType.CLASS_METHOD_RETURN_TYPE; 143 } else if (element instanceof SmaliField) { 144 return UsageType.CLASS_FIELD_DECLARATION; 145 } else if (element instanceof SmaliCatchStatement) { 146 return UsageType.CLASS_CATCH_CLAUSE_PARAMETER_DECLARATION; 147 } else if (element instanceof SmaliLocalDebugStatement) { 148 return UsageType.CLASS_LOCAL_VAR_DECLARATION; 149 } else if (element instanceof SmaliAnnotation) { 150 return UsageType.ANNOTATION; 151 } else if (element instanceof SmaliLiteral) { 152 return LITERAL; 153 } 154 element = element.getParent(); 155 } 156 return UsageType.UNCLASSIFIED; 157 } 158 159 @Nullable findFieldUsageType(@otNull PsiElement element)160 private UsageType findFieldUsageType(@NotNull PsiElement element) { 161 PsiElement originalElement = element; 162 163 while (element != null) { 164 element = element.getParent(); 165 166 if (element instanceof SmaliInstruction) { 167 Opcode opcode = ((SmaliInstruction) element).getOpcode(); 168 if (fieldReadInstructions.contains(opcode)) { 169 return UsageType.READ; 170 } else if (fieldWriteInstructions.contains(opcode)) { 171 return UsageType.WRITE; 172 } else if (opcode == Opcode.THROW_VERIFICATION_ERROR) { 173 return VERIFICATION_ERROR; 174 } 175 } if (element instanceof SmaliLiteral) { 176 return LITERAL; 177 } 178 } 179 return UsageType.UNCLASSIFIED; 180 } 181 182 @Nullable findMethodUsageType(@otNull PsiElement element)183 private UsageType findMethodUsageType(@NotNull PsiElement element) { 184 PsiElement originalElement = element; 185 186 while (element != null) { 187 element = element.getParent(); 188 189 if (element instanceof SmaliInstruction) { 190 Opcode opcode = ((SmaliInstruction) element).getOpcode(); 191 if (opcode == Opcode.THROW_VERIFICATION_ERROR) { 192 return VERIFICATION_ERROR; 193 } 194 } if (element instanceof SmaliLiteral) { 195 return LITERAL; 196 } 197 } 198 return UsageType.UNCLASSIFIED; 199 } 200 } 201