aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Gruver <bgruv@google.com>2016-02-21 10:00:36 -0800
committerBen Gruver <bgruv@google.com>2016-02-26 21:13:29 -0800
commitff4c85c5e4e380607fe1f89dc72db7339f77db8c (patch)
treebb44a7628fe1647c963211ea3e9ead114d3bbd34
parent6417e812e167c87c53655a6c27af35aea34faca5 (diff)
downloadsmali-ff4c85c5e4e380607fe1f89dc72db7339f77db8c.tar.gz
Provide better register type information
-rw-r--r--smalidea/build.gradle2
-rw-r--r--smalidea/src/main/java/org/jf/smalidea/debugging/SmaliCodeFragmentFactory.java9
-rw-r--r--smalidea/src/main/java/org/jf/smalidea/dexlib/SmalideaClassDef.java227
-rw-r--r--smalidea/src/main/java/org/jf/smalidea/dexlib/SmalideaField.java121
-rw-r--r--smalidea/src/main/java/org/jf/smalidea/dexlib/SmalideaMethod.java142
-rw-r--r--smalidea/src/main/java/org/jf/smalidea/dexlib/SmalideaMethodParameter.java9
-rw-r--r--smalidea/src/main/java/org/jf/smalidea/dexlib/analysis/SmalideaClassProvider.java36
-rw-r--r--smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliMethod.java4
-rw-r--r--smalidea/src/main/java/org/jf/smalidea/util/NameUtils.java6
-rw-r--r--smalidea/src/test/java/org/jf/smalidea/SmaliCodeFragmentFactoryTest.java143
10 files changed, 641 insertions, 58 deletions
diff --git a/smalidea/build.gradle b/smalidea/build.gradle
index 408b02c0..dcf6c5c1 100644
--- a/smalidea/build.gradle
+++ b/smalidea/build.gradle
@@ -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'
diff --git a/smalidea/src/main/java/org/jf/smalidea/debugging/SmaliCodeFragmentFactory.java b/smalidea/src/main/java/org/jf/smalidea/debugging/SmaliCodeFragmentFactory.java
index 469d27b9..abd5c90e 100644
--- a/smalidea/src/main/java/org/jf/smalidea/debugging/SmaliCodeFragmentFactory.java
+++ b/smalidea/src/main/java/org/jf/smalidea/debugging/SmaliCodeFragmentFactory.java
@@ -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;
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/SmalideaClassDef.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/SmalideaClassDef.java
new file mode 100644
index 00000000..11df39c3
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/SmalideaClassDef.java
@@ -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());
+ }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/SmalideaField.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/SmalideaField.java
new file mode 100644
index 00000000..bb022792
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/SmalideaField.java
@@ -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();
+ }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/SmalideaMethod.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/SmalideaMethod.java
index 3cfeebd6..a6c5c88a 100644
--- a/smalidea/src/main/java/org/jf/smalidea/dexlib/SmalideaMethod.java
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/SmalideaMethod.java
@@ -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;
- // TODO: cache this?
- return new MethodImplementation() {
- @Override public int getRegisterCount() {
- return psiMethod.getRegisterCount();
+ List<SmaliInstruction> instructions = smaliMethod.getInstructions();
+ if (instructions.size() == 0) {
+ return null;
}
- @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);
- }
- });
- }
+ // TODO: cache this?
+ return new MethodImplementation() {
+ @Override public int getRegisterCount() {
+ return smaliMethod.getRegisterCount();
+ }
- @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 Instruction> getInstructions() {
+ return Lists.transform(smaliMethod.getInstructions(),
+ new Function<SmaliInstruction, Instruction>() {
+ @Override
+ public Instruction apply(SmaliInstruction smaliInstruction) {
+ return SmalideaInstruction.of(smaliInstruction);
+ }
+ });
+ }
- @Nonnull @Override public Iterable<? extends DebugItem> getDebugItems() {
- // TODO: implement this
- return ImmutableList.of();
- }
- };
+ @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();
+ }
+ };
+ }
+ return null;
}
@Nonnull @Override public String getName() {
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/SmalideaMethodParameter.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/SmalideaMethodParameter.java
index 68e3aa58..e9e26a7c 100644
--- a/smalidea/src/main/java/org/jf/smalidea/dexlib/SmalideaMethodParameter.java
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/SmalideaMethodParameter.java
@@ -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());
}
}
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/analysis/SmalideaClassProvider.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/analysis/SmalideaClassProvider.java
new file mode 100644
index 00000000..e886a290
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/analysis/SmalideaClassProvider.java
@@ -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;
+ }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliMethod.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliMethod.java
index b1ae85e7..085585bb 100644
--- a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliMethod.java
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliMethod.java
@@ -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);
}
diff --git a/smalidea/src/main/java/org/jf/smalidea/util/NameUtils.java b/smalidea/src/main/java/org/jf/smalidea/util/NameUtils.java
index 6662b9e6..4a3cf293 100644
--- a/smalidea/src/main/java/org/jf/smalidea/util/NameUtils.java
+++ b/smalidea/src/main/java/org/jf/smalidea/util/NameUtils.java
@@ -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);
}
diff --git a/smalidea/src/test/java/org/jf/smalidea/SmaliCodeFragmentFactoryTest.java b/smalidea/src/test/java/org/jf/smalidea/SmaliCodeFragmentFactoryTest.java
index e987a397..c61d3e44 100644
--- a/smalidea/src/test/java/org/jf/smalidea/SmaliCodeFragmentFactoryTest.java
+++ b/smalidea/src/test/java/org/jf/smalidea/SmaliCodeFragmentFactoryTest.java
@@ -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();
+ }
}