Provide better register type information

This commit is contained in:
Ben Gruver 2016-02-21 10:00:36 -08:00
parent 6417e812e1
commit ff4c85c5e4
10 changed files with 645 additions and 62 deletions

View File

@ -126,7 +126,7 @@ repositories {
}
dependencies {
compile 'org.smali:smali:2.1.2'
compile 'org.smali:smali:2.1.2-205bf333'
compile 'org.antlr:antlr-runtime:3.5.2'
compile 'com.google.code.gson:gson:2.3.1'

View File

@ -58,6 +58,7 @@ import org.jf.smalidea.SmaliLanguage;
import org.jf.smalidea.debugging.value.LazyValue;
import org.jf.smalidea.psi.impl.SmaliInstruction;
import org.jf.smalidea.psi.impl.SmaliMethod;
import org.jf.smalidea.util.NameUtils;
import org.jf.smalidea.util.PsiUtil;
import java.lang.reflect.Constructor;
@ -225,10 +226,12 @@ public class SmaliCodeFragmentFactory extends DefaultCodeFragmentFactory {
case RegisterType.UNINIT_REF:
case RegisterType.UNINIT_THIS:
case RegisterType.REFERENCE:
variablesText.append("Object v").append(i).append(";\n");
registerMap.put("v" + i, "Ljava/lang/Object;");
String smaliType = registerType.type.getType();
String javaType = NameUtils.smaliToJavaType(smaliType);
variablesText.append(javaType).append(" v").append(i).append(";\n");
registerMap.put("v" + i, smaliType);
if (parameterRegisterNumber >= 0) {
variablesText.append("Object p").append(parameterRegisterNumber).append(";\n");
variablesText.append(javaType).append(" p").append(parameterRegisterNumber).append(";\n");
registerMap.put("p" + parameterRegisterNumber, "Ljava/lang/Object;");
}
break;

View File

@ -0,0 +1,227 @@
/*
* Copyright 2016, 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.dexlib;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiModifierList;
import org.jf.dexlib2.AccessFlags;
import org.jf.dexlib2.base.reference.BaseTypeReference;
import org.jf.dexlib2.iface.Annotation;
import org.jf.dexlib2.iface.ClassDef;
import org.jf.dexlib2.iface.Field;
import org.jf.dexlib2.iface.Method;
import org.jf.smalidea.util.NameUtils;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
public class SmalideaClassDef extends BaseTypeReference implements ClassDef {
private final PsiClass psiClass;
public SmalideaClassDef(PsiClass psiClass) {
this.psiClass = psiClass;
}
@Override public int getAccessFlags() {
PsiModifierList modifierList = psiClass.getModifierList();
int flags = 0;
if (modifierList == null) {
return flags;
}
if (modifierList.hasModifierProperty("public")) {
flags |= AccessFlags.PUBLIC.getValue();
}
if (modifierList.hasModifierProperty("final")) {
flags |= AccessFlags.FINAL.getValue();
}
if (modifierList.hasModifierProperty("abstract")) {
flags |= AccessFlags.ABSTRACT.getValue();
}
if (psiClass.isInterface()) {
flags |= AccessFlags.INTERFACE.getValue();
}
if (psiClass.isEnum()) {
flags |= AccessFlags.ENUM.getValue();
}
if (psiClass.isAnnotationType()) {
flags |= AccessFlags.ANNOTATION.getValue();
}
return flags;
}
@Nonnull @Override public String getType() {
// TODO: properly handle inner classes..
String javaName = psiClass.getQualifiedName();
if (javaName == null) {
throw new RuntimeException("I don't know what to do here... Is this even possible?");
}
return NameUtils.javaToSmaliType(javaName);
}
@Nullable @Override public String getSuperclass() {
PsiClass superClass = psiClass.getSuperClass();
if (superClass == null) {
return null;
}
String javaName = superClass.getQualifiedName();
if (javaName == null) {
throw new RuntimeException("I don't know what to do here... Is this even possible?");
}
return NameUtils.javaToSmaliType(javaName);
}
@Nonnull @Override public List<String> getInterfaces() {
List<String> interfaceList = Lists.newArrayList();
PsiClass[] interfaces = psiClass.getInterfaces();
if (interfaces == null) {
return interfaceList;
}
for (PsiClass psiClass: interfaces) {
String javaName = psiClass.getQualifiedName();
if (javaName == null) {
throw new RuntimeException("I don't know what to do here... Is this even possible?");
}
interfaceList.add(NameUtils.javaToSmaliType(javaName));
}
return interfaceList;
}
@Nullable @Override public String getSourceFile() {
return null;
}
@Nonnull @Override public Set<? extends Annotation> getAnnotations() {
return ImmutableSet.of();
}
@Nonnull @Override public Iterable<? extends Field> getStaticFields() {
return Iterables.transform(
Iterables.filter(Arrays.asList(psiClass.getFields()), new Predicate<PsiField>() {
@Override public boolean apply(PsiField psiField) {
PsiModifierList modifierList = psiField.getModifierList();
if (modifierList == null) {
return false;
}
return modifierList.hasModifierProperty("static");
}
}),
new Function<PsiField, Field>() {
@Nullable @Override public Field apply(@Nullable PsiField psiField) {
return new SmalideaField(psiField);
}
});
}
@Nonnull @Override public Iterable<? extends Field> getInstanceFields() {
return Iterables.transform(
Iterables.filter(Arrays.asList(psiClass.getFields()), new Predicate<PsiField>() {
@Override public boolean apply(PsiField psiField) {
PsiModifierList modifierList = psiField.getModifierList();
if (modifierList == null) {
return true;
}
return !modifierList.hasModifierProperty("static");
}
}),
new Function<PsiField, Field>() {
@Nullable @Override public Field apply(@Nullable PsiField psiField) {
return new SmalideaField(psiField);
}
});
}
@Nonnull @Override public Iterable<? extends Field> getFields() {
return Iterables.concat(getStaticFields(), getInstanceFields());
}
@Nonnull @Override public Iterable<? extends Method> getDirectMethods() {
return Iterables.transform(
Iterables.filter(
Iterables.concat(
Arrays.asList(psiClass.getConstructors()),
Arrays.asList(psiClass.getMethods())),
new Predicate<PsiMethod>() {
@Override public boolean apply(PsiMethod psiMethod) {
PsiModifierList modifierList = psiMethod.getModifierList();
return modifierList.hasModifierProperty("static") ||
modifierList.hasModifierProperty("private") ||
modifierList.hasModifierProperty("constructor");
}
}),
new Function<PsiMethod, Method>() {
@Nullable @Override public Method apply(PsiMethod psiMethod) {
return new SmalideaMethod(psiMethod);
}
});
}
@Nonnull @Override public Iterable<? extends Method> getVirtualMethods() {
return Iterables.transform(
Iterables.filter(Arrays.asList(psiClass.getMethods()), new Predicate<PsiMethod>() {
@Override public boolean apply(PsiMethod psiMethod) {
PsiModifierList modifierList = psiMethod.getModifierList();
return !modifierList.hasModifierProperty("static") &&
!modifierList.hasModifierProperty("private") &&
!modifierList.hasModifierProperty("constructor");
}
}),
new Function<PsiMethod, Method>() {
@Nullable @Override public Method apply(PsiMethod psiMethod) {
return new SmalideaMethod(psiMethod);
}
});
}
@Nonnull @Override public Iterable<? extends Method> getMethods() {
return Iterables.concat(getDirectMethods(), getVirtualMethods());
}
}

View File

@ -0,0 +1,121 @@
/*
* Copyright 2016, 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.dexlib;
import com.google.common.collect.ImmutableSet;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiModifierList;
import org.jf.dexlib2.AccessFlags;
import org.jf.dexlib2.base.reference.BaseFieldReference;
import org.jf.dexlib2.iface.Annotation;
import org.jf.dexlib2.iface.Field;
import org.jf.dexlib2.iface.value.EncodedValue;
import org.jf.smalidea.psi.impl.SmaliField;
import org.jf.smalidea.util.NameUtils;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Set;
public class SmalideaField extends BaseFieldReference implements Field {
private final PsiField psiField;
public SmalideaField(PsiField psiField) {
this.psiField = psiField;
}
@Override public int getAccessFlags() {
if (psiField instanceof SmaliField) {
return ((SmaliField)psiField).getModifierList().getAccessFlags();
} else {
int flags = 0;
PsiModifierList modifierList = psiField.getModifierList();
if (modifierList == null) {
return flags;
}
if (modifierList.hasModifierProperty("public")) {
flags |= AccessFlags.PUBLIC.getValue();
} else if (modifierList.hasModifierProperty("protected")) {
flags |= AccessFlags.PROTECTED.getValue();
} else if (modifierList.hasModifierProperty("private")) {
flags |= AccessFlags.PRIVATE.getValue();
}
if (modifierList.hasModifierProperty("static")) {
flags |= AccessFlags.STATIC.getValue();
}
if (modifierList.hasModifierProperty("final")) {
flags |= AccessFlags.FINAL.getValue();
}
if (modifierList.hasModifierProperty("volatile")) {
flags |= AccessFlags.VOLATILE.getValue();
}
// TODO: how do we tell if it's an enum?
return flags;
}
}
@Nonnull @Override public String getDefiningClass() {
PsiClass containingClass = psiField.getContainingClass();
if (containingClass == null) {
throw new RuntimeException("I don't know what to do here... Is this even possible?");
}
String javaName = containingClass.getQualifiedName();
if (javaName == null) {
throw new RuntimeException("I don't know what to do here... Is this even possible?");
}
return NameUtils.javaToSmaliType(javaName);
}
@Nonnull @Override public String getName() {
return psiField.getNameIdentifier().getText();
}
@Nonnull @Override public String getType() {
String javaName = psiField.getType().getCanonicalText();
return NameUtils.javaToSmaliType(javaName);
}
@Nullable @Override public EncodedValue getInitialValue() {
// TODO: implement this. Not needed for method analysis
return null;
}
@Nonnull @Override public Set<? extends Annotation> getAnnotations() {
// TODO: implement this. Not needed for method analysis
return ImmutableSet.of();
}
}

View File

@ -36,9 +36,12 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiModifierList;
import com.intellij.psi.PsiParameter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jf.dexlib2.AccessFlags;
import org.jf.dexlib2.base.reference.BaseMethodReference;
import org.jf.dexlib2.iface.*;
import org.jf.dexlib2.iface.debug.DebugItem;
@ -47,7 +50,6 @@ import org.jf.smalidea.dexlib.instruction.SmalideaInstruction;
import org.jf.smalidea.psi.impl.SmaliCatchStatement;
import org.jf.smalidea.psi.impl.SmaliInstruction;
import org.jf.smalidea.psi.impl.SmaliMethod;
import org.jf.smalidea.psi.impl.SmaliMethodParameter;
import org.jf.smalidea.util.NameUtils;
import javax.annotation.Nonnull;
@ -56,9 +58,9 @@ import java.util.List;
import java.util.Set;
public class SmalideaMethod extends BaseMethodReference implements Method {
private final SmaliMethod psiMethod;
private final PsiMethod psiMethod;
public SmalideaMethod(@NotNull SmaliMethod psiMethod) {
public SmalideaMethod(@NotNull PsiMethod psiMethod) {
this.psiMethod = psiMethod;
}
@ -71,21 +73,72 @@ public class SmalideaMethod extends BaseMethodReference implements Method {
}
@Nonnull @Override public List<? extends MethodParameter> getParameters() {
SmaliMethodParameter[] parameters = psiMethod.getParameterList().getParameters();
PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
return Lists.transform(Arrays.asList(parameters), new Function<SmaliMethodParameter, MethodParameter>() {
return Lists.transform(Arrays.asList(parameters), new Function<PsiParameter, MethodParameter>() {
@Nullable @Override
public MethodParameter apply(@Nullable SmaliMethodParameter smaliParameter) {
if (smaliParameter == null) {
public MethodParameter apply(@Nullable PsiParameter psiParameter) {
if (psiParameter == null) {
return null;
}
return new SmalideaMethodParameter(smaliParameter);
return new SmalideaMethodParameter(psiParameter);
}
});
}
@Override public int getAccessFlags() {
return psiMethod.getModifierList().getAccessFlags();
if (psiMethod instanceof SmaliMethod) {
return ((SmaliMethod)psiMethod).getModifierList().getAccessFlags();
} else {
int flags = 0;
PsiModifierList modifierList = psiMethod.getModifierList();
if (modifierList.hasModifierProperty("public")) {
flags |= AccessFlags.PUBLIC.getValue();
} else if (modifierList.hasModifierProperty("protected")) {
flags |= AccessFlags.PROTECTED.getValue();
} else if (modifierList.hasModifierProperty("private")) {
flags |= AccessFlags.PRIVATE.getValue();
}
if (modifierList.hasModifierProperty("static")) {
flags |= AccessFlags.STATIC.getValue();
}
if (modifierList.hasModifierProperty("final")) {
flags |= AccessFlags.FINAL.getValue();
}
boolean isNative = false;
if (modifierList.hasModifierProperty("native")) {
flags |= AccessFlags.NATIVE.getValue();
isNative = true;
}
if (modifierList.hasModifierProperty("synchronized")) {
if (isNative) {
flags |= AccessFlags.SYNCHRONIZED.getValue();
} else {
flags |= AccessFlags.DECLARED_SYNCHRONIZED.getValue();
}
}
if (psiMethod.isVarArgs()) {
flags |= AccessFlags.VARARGS.getValue();
}
if (modifierList.hasModifierProperty("abstract")) {
flags |= AccessFlags.ABSTRACT.getValue();
}
if (modifierList.hasModifierProperty("strictfp")) {
flags |= AccessFlags.STRICTFP.getValue();
}
if (psiMethod.isConstructor()) {
flags |= AccessFlags.CONSTRUCTOR.getValue();
}
return flags;
}
}
@Nonnull @Override public Set<? extends Annotation> getAnnotations() {
@ -94,44 +147,49 @@ public class SmalideaMethod extends BaseMethodReference implements Method {
}
@Nullable @Override public MethodImplementation getImplementation() {
List<SmaliInstruction> instructions = psiMethod.getInstructions();
if (instructions.size() == 0) {
return null;
if (psiMethod instanceof SmaliMethod) {
final SmaliMethod smaliMethod = (SmaliMethod)this.psiMethod;
List<SmaliInstruction> instructions = smaliMethod.getInstructions();
if (instructions.size() == 0) {
return null;
}
// TODO: cache this?
return new MethodImplementation() {
@Override public int getRegisterCount() {
return smaliMethod.getRegisterCount();
}
@Nonnull @Override public Iterable<? extends Instruction> getInstructions() {
return Lists.transform(smaliMethod.getInstructions(),
new Function<SmaliInstruction, Instruction>() {
@Override
public Instruction apply(SmaliInstruction smaliInstruction) {
return SmalideaInstruction.of(smaliInstruction);
}
});
}
@Nonnull @Override public List<? extends TryBlock<? extends ExceptionHandler>> getTryBlocks() {
return Lists.transform(smaliMethod.getCatchStatements(),
new Function<SmaliCatchStatement, TryBlock<? extends ExceptionHandler>>() {
@Override
public TryBlock<? extends ExceptionHandler> apply(
SmaliCatchStatement smaliCatchStatement) {
assert smaliCatchStatement != null;
return new SmalideaTryBlock(smaliCatchStatement);
}
});
}
@Nonnull @Override public Iterable<? extends DebugItem> getDebugItems() {
// TODO: implement this
return ImmutableList.of();
}
};
}
// TODO: cache this?
return new MethodImplementation() {
@Override public int getRegisterCount() {
return psiMethod.getRegisterCount();
}
@Nonnull @Override public Iterable<? extends Instruction> getInstructions() {
return Lists.transform(psiMethod.getInstructions(),
new Function<SmaliInstruction, Instruction>() {
@Override
public Instruction apply(SmaliInstruction smaliInstruction) {
return SmalideaInstruction.of(smaliInstruction);
}
});
}
@Nonnull @Override public List<? extends TryBlock<? extends ExceptionHandler>> getTryBlocks() {
return Lists.transform(psiMethod.getCatchStatements(),
new Function<SmaliCatchStatement, TryBlock<? extends ExceptionHandler>>() {
@Override
public TryBlock<? extends ExceptionHandler> apply(
SmaliCatchStatement smaliCatchStatement) {
assert smaliCatchStatement != null;
return new SmalideaTryBlock(smaliCatchStatement);
}
});
}
@Nonnull @Override public Iterable<? extends DebugItem> getDebugItems() {
// TODO: implement this
return ImmutableList.of();
}
};
return null;
}
@Nonnull @Override public String getName() {

View File

@ -32,19 +32,20 @@
package org.jf.smalidea.dexlib;
import com.google.common.collect.ImmutableSet;
import com.intellij.psi.PsiParameter;
import org.jetbrains.annotations.Nullable;
import org.jf.dexlib2.base.BaseMethodParameter;
import org.jf.dexlib2.iface.Annotation;
import org.jf.smalidea.psi.impl.SmaliMethodParameter;
import org.jf.smalidea.util.NameUtils;
import org.jf.smalidea.util.StringUtils;
import javax.annotation.Nonnull;
import java.util.Set;
public class SmalideaMethodParameter extends BaseMethodParameter {
private final SmaliMethodParameter psiParameter;
private final PsiParameter psiParameter;
public SmalideaMethodParameter(SmaliMethodParameter psiParameter) {
public SmalideaMethodParameter(PsiParameter psiParameter) {
this.psiParameter = psiParameter;
}
@ -58,6 +59,6 @@ public class SmalideaMethodParameter extends BaseMethodParameter {
}
@Nonnull @Override public String getType() {
return psiParameter.getTypeElement().getText();
return NameUtils.javaToSmaliType(psiParameter.getType().getCanonicalText());
}
}

View File

@ -0,0 +1,36 @@
package org.jf.smalidea.dexlib.analysis;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiClass;
import com.intellij.psi.impl.ResolveScopeManager;
import org.jf.dexlib2.analysis.ClassProvider;
import org.jf.dexlib2.iface.ClassDef;
import org.jf.smalidea.dexlib.SmalideaClassDef;
import org.jf.smalidea.util.NameUtils;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class SmalideaClassProvider implements ClassProvider {
private final Project project;
private final VirtualFile file;
public SmalideaClassProvider(@Nonnull Project project, @Nonnull VirtualFile file) {
this.project = project;
this.file = file;
}
@Nullable @Override public ClassDef getClassDef(String type) {
ResolveScopeManager manager = ResolveScopeManager.getInstance(project);
JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
PsiClass psiClass = facade.findClass(NameUtils.smaliToJavaType(type),
manager.getDefaultResolveScope(file));
if (psiClass != null) {
return new SmalideaClassDef(psiClass);
}
return null;
}
}

View File

@ -53,6 +53,7 @@ import org.jf.dexlib2.analysis.AnalysisException;
import org.jf.dexlib2.analysis.ClassPath;
import org.jf.dexlib2.analysis.MethodAnalyzer;
import org.jf.smalidea.dexlib.SmalideaMethod;
import org.jf.smalidea.dexlib.analysis.SmalideaClassProvider;
import org.jf.smalidea.psi.SmaliElementTypes;
import org.jf.smalidea.psi.iface.SmaliModifierListOwner;
import org.jf.smalidea.psi.stub.SmaliMethodStub;
@ -316,7 +317,8 @@ public class SmaliMethod extends SmaliStubBasedPsiElement<SmaliMethodStub>
if (!PsiTreeUtil.hasErrorElements(this)) {
ClassPath classPath;
try {
classPath = new ClassPath();
classPath = new ClassPath(
new SmalideaClassProvider(getProject(), getContainingFile().getVirtualFile()));
} catch (IOException ex) {
throw new RuntimeException(ex);
}

View File

@ -163,6 +163,12 @@ public class NameUtils {
}
}
return;
case 'U':
if (smaliType.equals("Ujava/lang/Object;")) {
dest.append("java.lang.Object");
return;
}
// fall through
default:
throw new RuntimeException("Invalid smali type: " + smaliType);
}

View File

@ -32,6 +32,7 @@
package org.jf.smalidea;
import com.google.common.collect.Sets;
import com.intellij.codeInsight.CodeInsightTestCase;
import com.intellij.codeInsight.completion.CodeCompletionHandlerBase;
import com.intellij.codeInsight.completion.CompletionType;
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
@ -44,11 +45,13 @@ import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.impl.EditorImpl;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.JavaCodeFragment;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
import com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl;
import org.jetbrains.annotations.NotNull;
import org.jf.smalidea.debugging.SmaliCodeFragmentFactory;
import org.jf.smalidea.psi.impl.SmaliFile;
@ -57,20 +60,20 @@ import org.junit.Assert;
import java.util.HashSet;
import java.util.List;
public class SmaliCodeFragmentFactoryTest extends LightCodeInsightFixtureTestCase {
private static final String testClass =
public class SmaliCodeFragmentFactoryTest extends CodeInsightTestCase {
private static final String completionTestClass =
".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
".method public getRandomParentType(I)I\n" +
" .registers 4\n" +
" .param p1, \"edge\" # I\n" +
"\n" +
" .prologue\n" +
" const/4 v1, 0x2\n" +
" const/4 v1, 0x2\n" + // 0
"\n" +
" .line 179\n" +
" if-nez p1, :cond_5\n" +
"\n" +
" move v0, v1\n" +
" move v0, v1\n" + // 2
"\n" +
" .line 185\n" +
" :goto_4\n" +
@ -83,7 +86,7 @@ public class SmaliCodeFragmentFactoryTest extends LightCodeInsightFixtureTestCas
" .line 183\n" +
" sget-object v0, Lorg/jf/Penroser/PenroserApp;->random:Ljava/util/Random;\n" +
"\n" +
" const/4 v1, 0x3\n" +
" const/4 v1, 0x3\n" + // 6
"\n" +
" invoke-virtual {v0, v1}, Ljava/util/Random;->nextInt(I)I\n" +
"\n" +
@ -103,7 +106,7 @@ public class SmaliCodeFragmentFactoryTest extends LightCodeInsightFixtureTestCas
".end method";
public void testCompletion() throws NoDataException {
SmaliFile smaliFile = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali", testClass);
SmaliFile smaliFile = (SmaliFile)configureByText(SmaliFileType.INSTANCE, completionTestClass);
PsiElement context = smaliFile.getPsiClass().getMethods()[0].getInstructions().get(0);
assertCompletionContains("v", context, new String[] {"v2", "v3"}, new String[] {"v0", "v1", "p0", "p1"});
@ -112,6 +115,112 @@ public class SmaliCodeFragmentFactoryTest extends LightCodeInsightFixtureTestCas
context = smaliFile.getPsiClass().getMethods()[0].getInstructions().get(2);
assertCompletionContains("v", context, new String[] {"v1", "v2", "v3"}, new String[] {"v0", "p0", "p1"});
assertCompletionContains("p", context, new String[] {"p0", "p1"}, new String[] {"v0", "v1", "v2", "v3"});
context = smaliFile.getPsiClass().getMethods()[0].getInstructions().get(6);
assertCompletionContains("v", context, new String[] {"v0", "v1", "v2", "v3"}, new String[] {"p0", "p1"});
assertCompletionContains("p", context, new String[] {"p0", "p1"}, new String[] {});
}
private static final String registerTypeTestText = "" +
".class public LRegisterTypeTest;\n" +
".super Ljava/lang/Object;\n" +
"\n" +
"# virtual methods\n" +
".method public blah()V\n" +
" .registers 6\n" +
"\n" +
" .prologue\n" +
" const/16 v3, 0xa\n" +
"\n" +
" .line 7\n" +
" new-instance v0, Ljava/util/Random;\n" +
"\n" +
" invoke-direct {v0}, Ljava/util/Random;-><init>()V\n" +
"\n" +
" .line 9\n" +
" invoke-virtual {v0, v3}, Ljava/util/Random;->nextInt(I)I\n" +
"\n" +
" move-result v1\n" +
"\n" +
" const/4 v2, 0x5\n" +
"\n" +
" if-le v1, v2, :cond_26\n" +
"\n" +
" .line 10\n" +
" new-instance v1, Ljava/security/SecureRandom;\n" +
"\n" +
" invoke-direct {v1}, Ljava/security/SecureRandom;-><init>()V\n" +
"\n" +
" .line 14\n" +
" :goto_13\n" +
" sget-o<ref>bject v2, Ljava/lang/System;->out:Ljava/io/PrintStream;\n" +
"\n" +
" invoke-virtual {v1, v3}, Ljava/util/Random;->nextInt(I)I\n" +
"\n" +
" move-result v1\n" +
"\n" +
" invoke-virtual {v2, v1}, Ljava/io/PrintStream;->println(I)V\n" +
"\n" +
" .line 15\n" +
" sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;\n" +
"\n" +
" invoke-virtual {v0}, Ljava/lang/Object;->toString()Ljava/lang/String;\n" +
"\n" +
" move-result-object v0\n" +
"\n" +
" invoke-virtual {v1, v0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V\n" +
"\n" +
" .line 16\n" +
" return-void\n" +
"\n" +
" .line 12\n" +
" :cond_26\n" +
" invoke-virtual {p0}, LRegisterTypeTest;->getSerializable()Ljava/io/Serializable;\n" +
"\n" +
" move-result-object v1\n" +
"\n" +
" move-object v4, v1\n" +
"\n" +
" move-object v1, v0\n" +
"\n" +
" move-object v0, v4\n" +
"\n" +
" goto :goto_13\n" +
".end method\n" +
"\n" +
".method public getSerializable()Ljava/io/Serializable;\n" +
" .registers 2\n" +
"\n" +
" .prologue\n" +
" .line 19\n" +
" new-instance v0, Ljava/util/Random;\n" +
"\n" +
" invoke-direct {v0}, Ljava/util/Random;-><init>()V\n" +
"\n" +
" return-object v0\n" +
".end method\n";
public void testRegisterType() throws NoDataException {
SmaliFile smaliFile = (SmaliFile)configureByText(SmaliFileType.INSTANCE,
registerTypeTestText.replace("<ref>", ""));
int refOffset = registerTypeTestText.indexOf("<ref>");
PsiElement context = smaliFile.findElementAt(refOffset);
assertVariableType(context.getParent(), "v1", "java.util.Random");
assertVariableType(context.getParent(), "v0", "java.io.Serializable");
}
public void testUnknownClass() {
String modifiedText = registerTypeTestText.replace("Random", "Rnd");
SmaliFile smaliFile = (SmaliFile)configureByText(SmaliFileType.INSTANCE,
modifiedText.replace("<ref>", ""));
int refOffset = modifiedText.indexOf("<ref>");
PsiElement context = smaliFile.findElementAt(refOffset);
assertVariableType(context.getParent(), "v1", "java.lang.Object");
assertVariableType(context.getParent(), "v0", "java.lang.Object");
}
private void assertCompletionContains(String completionText, PsiElement context, String[] expectedItems,
@ -138,6 +247,21 @@ public class SmaliCodeFragmentFactoryTest extends LightCodeInsightFixtureTestCas
Assert.assertTrue(expectedSet.size() == 0);
}
private void assertVariableType(PsiElement context, String variableName, String expectedType) {
SmaliCodeFragmentFactory codeFragmentFactory = new SmaliCodeFragmentFactory();
JavaCodeFragment fragment = codeFragmentFactory.createCodeFragment(
new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, variableName),
context, getProject());
Editor editor = createEditor(fragment.getVirtualFile());
editor.getCaretModel().moveToOffset(1);
PsiElement element = fragment.findElementAt(0);
Assert.assertTrue(element.getParent() instanceof PsiReferenceExpressionImpl);
PsiReferenceExpressionImpl reference = (PsiReferenceExpressionImpl)element.getParent();
Assert.assertEquals(expectedType, reference.getType().getCanonicalText());
}
protected Editor createEditor(@NotNull VirtualFile file) {
PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
Editor editor = FileEditorManager.getInstance(getProject()).openTextEditor(
@ -147,4 +271,9 @@ public class SmaliCodeFragmentFactoryTest extends LightCodeInsightFixtureTestCas
((EditorImpl)editor).setCaretActive();
return editor;
}
@Override
protected Sdk getTestProjectJdk() {
return JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk();
}
}