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