SmaliMethod.java revision 7f6afa6a259cfcc06ce962029401189d080663a0
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 if (isConstructor()) return null; 102 return getMethodPrototype().getReturnType(); 103 } 104 105 @Nullable @Override public PsiTypeElement getReturnTypeElement() { 106 if (isConstructor()) return null; 107 return getMethodPrototype().getReturnTypeElement(); 108 } 109 110 @NotNull @Override public SmaliMethodParamList getParameterList() { 111 return getMethodPrototype().getParameterList(); 112 } 113 114 @NotNull @Override public SmaliThrowsList getThrowsList() { 115 return getRequiredStubOrPsiChild(SmaliElementTypes.THROWS_LIST); 116 } 117 118 @Nullable @Override public PsiCodeBlock getBody() { 119 // not applicable 120 return null; 121 } 122 123 @NotNull public List<SmaliInstruction> getInstructions() { 124 return findChildrenByType(SmaliElementTypes.INSTRUCTION); 125 } 126 127 @NotNull public List<SmaliCatchStatement> getCatchStatements() { 128 return Arrays.asList(findChildrenByClass(SmaliCatchStatement.class)); 129 } 130 131 @Nullable public SourcePosition getSourcePositionForCodeOffset(int offset) { 132 for (SmaliInstruction instruction: getInstructions()) { 133 if (instruction.getOffset() >= offset) { 134 return SourcePosition.createFromElement(instruction); 135 } 136 } 137 return null; 138 } 139 140 public int getOffsetForLine(int line) { 141 PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject()); 142 final Document document = documentManager.getDocument(getContainingFile()); 143 if (document == null) { 144 return -1; 145 } 146 147 for (final SmaliInstruction instruction: getInstructions()) { 148 int curLine = document.getLineNumber(instruction.getTextOffset()); 149 if (curLine >= line) { 150 return instruction.getOffset(); 151 } 152 } 153 return -1; 154 } 155 156 public int getRegisterCount() { 157 SmaliRegistersStatement registersStatement = findChildByClass(SmaliRegistersStatement.class); 158 if (registersStatement == null) { 159 return 0; 160 } 161 return registersStatement.getRegisterCount(); 162 } 163 164 public int getParameterRegisterCount() { 165 int parameterRegisterCount = getMethodPrototype().getParameterList().getParameterRegisterCount(); 166 if (!isStatic()) { 167 parameterRegisterCount++; 168 } 169 return parameterRegisterCount; 170 } 171 172 @NotNull public SmaliParameterStatement[] getParameterStatements() { 173 return findChildrenByClass(SmaliParameterStatement.class); 174 } 175 176 @Override public boolean isConstructor() { 177 return hasModifierProperty("constructor") && !hasModifierProperty("static"); 178 } 179 180 public boolean isStatic() { 181 return hasModifierProperty("static"); 182 } 183 184 @Override public boolean isVarArgs() { 185 return hasModifierProperty("varargs"); 186 } 187 188 @NotNull @Override public MethodSignature getSignature(@NotNull PsiSubstitutor substitutor) { 189 return MethodSignatureBackedByPsiMethod.create(this, substitutor); 190 } 191 192 @Nullable @Override public SmaliMemberName getNameIdentifier() { 193 return findChildByClass(SmaliMemberName.class); 194 } 195 196 @NotNull @Override public PsiMethod[] findSuperMethods() { 197 return PsiSuperMethodImplUtil.findSuperMethods(this); 198 } 199 200 @NotNull @Override public PsiMethod[] findSuperMethods(boolean checkAccess) { 201 return PsiSuperMethodImplUtil.findSuperMethods(this, checkAccess); 202 } 203 204 @NotNull @Override public PsiMethod[] findSuperMethods(PsiClass parentClass) { 205 return PsiSuperMethodImplUtil.findSuperMethods(this, parentClass); 206 } 207 208 @NotNull @Override 209 public List<MethodSignatureBackedByPsiMethod> findSuperMethodSignaturesIncludingStatic(boolean checkAccess) { 210 return PsiSuperMethodImplUtil.findSuperMethodSignaturesIncludingStatic(this, checkAccess); 211 } 212 213 @Nullable @Override public PsiMethod findDeepestSuperMethod() { 214 return PsiSuperMethodImplUtil.findDeepestSuperMethod(this); 215 } 216 217 @NotNull @Override public PsiMethod[] findDeepestSuperMethods() { 218 return PsiSuperMethodImplUtil.findDeepestSuperMethods(this); 219 } 220 221 @NotNull @Override public SmaliModifierList getModifierList() { 222 return getRequiredStubOrPsiChild(SmaliElementTypes.MODIFIER_LIST); 223 } 224 225 @Override public PsiElement setName(@NonNls @NotNull String name) throws IncorrectOperationException { 226 // TODO: implement this 227 throw new IncorrectOperationException(); 228 } 229 230 @NotNull @Override public HierarchicalMethodSignature getHierarchicalMethodSignature() { 231 return PsiSuperMethodImplUtil.getHierarchicalMethodSignature(this); 232 } 233 234 @Nullable @Override public PsiDocComment getDocComment() { 235 // not applicable 236 return null; 237 } 238 239 @Override public boolean isDeprecated() { 240 return PsiImplUtil.isDeprecatedByAnnotation(this); 241 } 242 243 @Nullable @Override public PsiTypeParameterList getTypeParameterList() { 244 // TODO: (generics) implement this 245 return null; 246 } 247 248 @NotNull @Override public PsiTypeParameter[] getTypeParameters() { 249 // TODO: (generics) implement this 250 return new PsiTypeParameter[0]; 251 } 252 253 @Nullable @Override public PsiClass getContainingClass() { 254 return (SmaliClass)getStubOrPsiParent(); 255 } 256 257 @Override public boolean hasModifierProperty(@ModifierConstant @NonNls @NotNull String name) { 258 return getModifierList().hasModifierProperty(name); 259 } 260 261 @Nullable @Override public SmaliAccessList getAccessFlagsNode() { 262 return findChildByClass(SmaliAccessList.class); 263 } 264 265 @NotNull @Override public SmaliAnnotation[] getAnnotations() { 266 return getStubOrPsiChildren(SmaliElementTypes.ANNOTATION, new SmaliAnnotation[0]); 267 } 268 269 @NotNull @Override public SmaliAnnotation[] getApplicableAnnotations() { 270 return getAnnotations(); 271 } 272 273 @Nullable @Override public SmaliAnnotation findAnnotation(@NotNull @NonNls String qualifiedName) { 274 for (SmaliAnnotation annotation: getAnnotations()) { 275 if (qualifiedName.equals(annotation.getQualifiedName())) { 276 return annotation; 277 } 278 } 279 return null; 280 } 281 282 @NotNull @Override public SmaliAnnotation addAnnotation(@NotNull @NonNls String qualifiedName) { 283 // TODO: implement this 284 return null; 285 } 286 287 private final Supplier<Map<String, SmaliLabel>> labelMap = Suppliers.memoize( 288 new Supplier<Map<String, SmaliLabel>>() { 289 @Override public Map<String, SmaliLabel> get() { 290 Map<String, SmaliLabel> labelMap = Maps.newHashMap(); 291 for (SmaliLabel label: findChildrenByClass(SmaliLabel.class)) { 292 if (!labelMap.containsKey(label.getText())) { 293 labelMap.put(label.getText(), label); 294 } 295 } 296 return labelMap; 297 } 298 }); 299 300 @Nullable public SmaliLabel getLabel(String name) { 301 return labelMap.get().get(name); 302 } 303 304 private MethodAnalyzer methodAnalyzer = null; 305 306 @NotNull 307 public MethodAnalyzer getMethodAnalyzer() { 308 if (methodAnalyzer == null) { 309 ClassPath classPath; 310 try { 311 classPath = new ClassPath(); 312 } catch (IOException ex) { 313 throw new RuntimeException(ex); 314 } 315 316 methodAnalyzer = new MethodAnalyzer(classPath, new SmalideaMethod(SmaliMethod.this), null); 317 } 318 return methodAnalyzer; 319 } 320 321 @Override public void subtreeChanged() { 322 super.subtreeChanged(); 323 methodAnalyzer = null; 324 } 325} 326