Add initial support for finding class usages in smali code

This commit is contained in:
Ben Gruver 2015-03-08 12:06:17 -07:00
parent 343ec04252
commit 694fb7ca90
6 changed files with 249 additions and 1 deletions

View File

@ -26,6 +26,9 @@
implementation="org.jf.smalidea.debugging.SmaliDebuggerSupport"/>
<debugger.codeFragmentFactory implementation="org.jf.smalidea.debugging.SmaliCodeFragmentFactory"/>
<stubElementTypeHolder class="org.jf.smalidea.psi.SmaliElementTypes" />
<lang.findUsagesProvider language="smali"
implementationClass="org.jf.smalidea.findUsages.SmaliFindUsagesProvider"/>
<referencesSearch implementation="org.jf.smalidea.findUsages.SmaliClassReferenceSearcher"/>
</extensions>
<application-components>

View File

@ -0,0 +1,97 @@
/*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jf.smalidea.findUsages;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.QueryExecutorBase;
import com.intellij.openapi.progress.EmptyProgressIndicator;
import com.intellij.openapi.util.Computable;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiReference;
import com.intellij.psi.impl.search.LowLevelSearchUtil;
import com.intellij.psi.search.*;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.search.searches.ReferencesSearch.SearchParameters;
import com.intellij.util.Processor;
import com.intellij.util.text.StringSearcher;
import org.jetbrains.annotations.NotNull;
import org.jf.smalidea.util.NameUtils;
public class SmaliClassReferenceSearcher extends QueryExecutorBase<PsiReference, ReferencesSearch.SearchParameters> {
@Override public void processQuery(final SearchParameters queryParameters, final Processor<PsiReference> consumer) {
final PsiElement element = queryParameters.getElementToSearch();
if (!(element instanceof PsiClass)) {
return;
}
String qualifiedName = ApplicationManager.getApplication().runReadAction(
new Computable<String>() {
@Override public String compute() {
return ((PsiClass)element).getQualifiedName();
}
});
if (qualifiedName == null) {
return;
}
String smaliType = NameUtils.javaToSmaliType(qualifiedName);
final StringSearcher stringSearcher = new StringSearcher(smaliType, true, true, false, false);
final SingleTargetRequestResultProcessor processor = new SingleTargetRequestResultProcessor(element);
// TODO: is it possible to get a LocalSearchScope here? If so, how to handle it?
if (!(queryParameters.getEffectiveSearchScope() instanceof GlobalSearchScope)) {
return;
}
PsiSearchHelper helper = PsiSearchHelper.SERVICE.getInstance(element.getProject());
// TODO: limit search scope to only smali files. See, e.g. AnnotatedPackagesSearcher.PackageInfoFilesOnly
helper.processAllFilesWithWord(smaliType, (GlobalSearchScope)queryParameters.getEffectiveSearchScope(),
new Processor<PsiFile>() {
@Override
public boolean process(PsiFile file) {
LowLevelSearchUtil.processElementsContainingWordInElement(
new TextOccurenceProcessor() {
@Override public boolean execute(
@NotNull PsiElement element, int offsetInElement) {
return processor.processTextOccurrence(element, offsetInElement, consumer);
}
},
file, stringSearcher, true, new EmptyProgressIndicator());
return true;
}
}, true);
}
}

View File

@ -0,0 +1,81 @@
/*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jf.smalidea.findUsages;
import com.intellij.find.impl.HelpID;
import com.intellij.lang.LangBundle;
import com.intellij.lang.cacheBuilder.WordsScanner;
import com.intellij.lang.findUsages.FindUsagesProvider;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class SmaliFindUsagesProvider implements FindUsagesProvider {
@Override public boolean canFindUsagesFor(@NotNull PsiElement element) {
return element instanceof PsiClass;
}
@Nullable @Override public WordsScanner getWordsScanner() {
return new SmaliWordScanner();
}
@Nullable @Override public String getHelpId(@NotNull PsiElement element) {
return HelpID.FIND_CLASS_USAGES;
}
@NotNull @Override public String getType(@NotNull PsiElement element) {
return LangBundle.message("java.terms.class");
}
@NotNull @Override public String getDescriptiveName(@NotNull PsiElement element) {
PsiClass psiClass = (PsiClass)element;
String qualifiedName = psiClass.getQualifiedName();
if (qualifiedName != null) {
return qualifiedName;
}
return psiClass.getName();
}
@NotNull @Override public String getNodeText(@NotNull PsiElement element, boolean onlyQualifiedNames) {
PsiClass psiClass = (PsiClass)element;
String qualifiedName = psiClass.getQualifiedName();
if (qualifiedName != null) {
return qualifiedName;
} else if (onlyQualifiedNames) {
return "";
}
return psiClass.getName();
}
}

View File

@ -0,0 +1,66 @@
/*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jf.smalidea.findUsages;
import com.intellij.lang.cacheBuilder.WordOccurrence;
import com.intellij.lang.cacheBuilder.WordOccurrence.Kind;
import com.intellij.lang.cacheBuilder.WordsScanner;
import com.intellij.psi.tree.IElementType;
import com.intellij.util.Processor;
import org.jetbrains.annotations.NotNull;
import org.jf.smalidea.SmaliLexer;
import org.jf.smalidea.SmaliTokens;
public class SmaliWordScanner implements WordsScanner {
@Override public void processWords(CharSequence fileText, Processor<WordOccurrence> processor) {
SmaliLexer lexer = new SmaliLexer();
lexer.start(fileText);
IElementType type = lexer.getTokenType();
while (type != null) {
if (type == SmaliTokens.CLASS_DESCRIPTOR) {
processClassDescriptor(fileText, lexer.getTokenStart(), lexer.getTokenEnd(), processor);
}
lexer.advance();
type = lexer.getTokenType();
}
}
private void processClassDescriptor(CharSequence fileText, int tokenStart, int tokenEnd,
@NotNull Processor<WordOccurrence> processor) {
CharSequence tokenText = fileText.subSequence(tokenStart, tokenEnd);
assert tokenText.charAt(0) == 'L' && tokenText.charAt(tokenText.length()-1) == ';';
processor.process(new WordOccurrence(fileText, tokenStart, tokenEnd, Kind.CODE));
}
}

View File

@ -73,6 +73,7 @@ public class SmaliAnnotation extends SmaliStubBasedPsiElement<SmaliAnnotationStu
@Nullable @Override public PsiJavaCodeReferenceElement getNameReferenceElement() {
// TODO: we need to have a PsiAnnotationMethod implementation for methods in an annotation class (see PsiUtil.isAnnotationMethod and PsiImplUtil.findAttributeValue)
// TODO: alternately, we should implement findAttributeValue and findAttributeValue ourselves, instead of relying on PsiImplUtil (don't forget about finding default values..)
// TODO: ensure that PsiAnnotationMethodReferencesSearcher will pick up method usages based on our annotation elements
SmaliAnnotationStub stub = getStub();
if (stub != null) {
String qualifiedName = stub.getAnnotationType();

View File

@ -112,7 +112,7 @@ public class SmaliClassTypeElement extends SmaliTypeElement implements PsiJavaCo
}
@Override public boolean isReferenceTo(PsiElement element) {
if (!(element instanceof PsiClassType)) {
if (!(element instanceof PsiClass)) {
return false;
}
return element.getManager().areElementsEquivalent(element, resolve());