From 9bedfc8e3e51c2b2435aab425533dc5043c84a9e Mon Sep 17 00:00:00 2001 From: Ben Gruver Date: Wed, 25 Mar 2015 19:45:20 -0700 Subject: [PATCH] Add usage type support for field usages --- smali/src/main/antlr/smaliParser.g | 5 +- smalidea/src/main/antlr3/smalideaParser.g | 14 +-- .../findUsages/SmaliUsageTypeProvider.java | 38 ++++++ .../findUsages/FieldUsageTypeTest.java | 115 ++++++++++++++++++ smalidea/testData/InvalidEnumLiteral.txt | 22 ++-- 5 files changed, 172 insertions(+), 22 deletions(-) create mode 100644 smalidea/src/test/java/org/jf/smalidea/findUsages/FieldUsageTypeTest.java diff --git a/smali/src/main/antlr/smaliParser.g b/smali/src/main/antlr/smaliParser.g index aab52738..9fcf2f1d 100644 --- a/smali/src/main/antlr/smaliParser.g +++ b/smali/src/main/antlr/smaliParser.g @@ -687,9 +687,10 @@ subannotation : SUBANNOTATION_DIRECTIVE CLASS_DESCRIPTOR annotation_element* END_SUBANNOTATION_DIRECTIVE -> ^(I_SUBANNOTATION[$start, "I_SUBANNOTATION"] CLASS_DESCRIPTOR annotation_element*); +// TODO: how does dalvik handle a primitive or array type, or a non-enum type? enum_literal - : ENUM_DIRECTIVE reference_type_descriptor ARROW simple_name COLON reference_type_descriptor - -> ^(I_ENCODED_ENUM reference_type_descriptor simple_name reference_type_descriptor); + : ENUM_DIRECTIVE field_reference + -> ^(I_ENCODED_ENUM field_reference); type_field_method_literal : reference_type_descriptor diff --git a/smalidea/src/main/antlr3/smalideaParser.g b/smalidea/src/main/antlr3/smalideaParser.g index ac7192d5..0da263f2 100644 --- a/smalidea/src/main/antlr3/smalideaParser.g +++ b/smalidea/src/main/antlr3/smalideaParser.g @@ -693,7 +693,7 @@ array_literal enum_literal @init { Marker marker = mark(); } - : ENUM_DIRECTIVE reference_type_descriptor arrow simple_name colon reference_type_descriptor + : ENUM_DIRECTIVE fully_qualified_field { marker.done(SmaliElementTypes.LITERAL); }; catch [RecognitionException re] { recover(input, re); @@ -702,15 +702,9 @@ enum_literal type_field_method_literal @init { Marker marker = mark(); } - : ( reference_type_descriptor - ( arrow - ( member_name colon nonvoid_type_descriptor - | member_name method_prototype_reference - ) - | /* epsilon */ - ) - | primitive_type - | void_type) + : ( type_descriptor + | fully_qualified_field + | fully_qualified_method) { marker.done(SmaliElementTypes.LITERAL); }; catch [RecognitionException re] { recover(input, re); diff --git a/smalidea/src/main/java/org/jf/smalidea/findUsages/SmaliUsageTypeProvider.java b/smalidea/src/main/java/org/jf/smalidea/findUsages/SmaliUsageTypeProvider.java index a0069b86..13ca82a2 100644 --- a/smalidea/src/main/java/org/jf/smalidea/findUsages/SmaliUsageTypeProvider.java +++ b/smalidea/src/main/java/org/jf/smalidea/findUsages/SmaliUsageTypeProvider.java @@ -33,6 +33,7 @@ package org.jf.smalidea.findUsages; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiField; import com.intellij.psi.PsiReference; import com.intellij.psi.tree.IElementType; import com.intellij.usages.impl.rules.UsageType; @@ -63,6 +64,8 @@ public class SmaliUsageTypeProvider implements UsageTypeProvider { if (referenced != null) { if (referenced instanceof PsiClass) { return findClassUsageType(element); + } else if (referenced instanceof PsiField) { + return findFieldUsageType(element); } } } @@ -72,6 +75,18 @@ public class SmaliUsageTypeProvider implements UsageTypeProvider { private final Set newArrayInstructions = EnumSet.of(Opcode.FILLED_NEW_ARRAY, Opcode.NEW_ARRAY, Opcode.FILLED_NEW_ARRAY_RANGE); + private final Set fieldReadInstructions = EnumSet.of(Opcode.IGET, Opcode.IGET_BOOLEAN, Opcode.IGET_BYTE, + Opcode.IGET_CHAR, Opcode.IGET_OBJECT, Opcode.IGET_OBJECT_VOLATILE, Opcode.IGET_SHORT, Opcode.IGET_VOLATILE, + Opcode.IGET_WIDE, Opcode.IGET_WIDE_VOLATILE, Opcode.SGET, Opcode.SGET_BOOLEAN, Opcode.SGET_BYTE, + Opcode.SGET_CHAR, Opcode.SGET_OBJECT, Opcode.SGET_OBJECT_VOLATILE, Opcode.SGET_SHORT, Opcode.SGET_VOLATILE, + Opcode.SGET_WIDE, Opcode.SGET_WIDE_VOLATILE); + + private final Set fieldWriteInstructions = EnumSet.of(Opcode.IPUT, Opcode.IPUT_BOOLEAN, Opcode.IPUT_BYTE, + Opcode.IPUT_CHAR, Opcode.IPUT_OBJECT, Opcode.IPUT_OBJECT_VOLATILE, Opcode.IPUT_SHORT, Opcode.IPUT_VOLATILE, + Opcode.IPUT_WIDE, Opcode.IPUT_WIDE_VOLATILE, Opcode.SPUT, Opcode.SPUT_BOOLEAN, Opcode.SPUT_BYTE, + Opcode.SPUT_CHAR, Opcode.SPUT_OBJECT, Opcode.SPUT_OBJECT_VOLATILE, Opcode.SPUT_SHORT, Opcode.SPUT_VOLATILE, + Opcode.SPUT_WIDE, Opcode.SPUT_WIDE_VOLATILE); + @Nullable private UsageType findClassUsageType(@NotNull PsiElement element) { PsiElement originalElement = element; @@ -141,4 +156,27 @@ public class SmaliUsageTypeProvider implements UsageTypeProvider { } return UsageType.UNCLASSIFIED; } + + @Nullable + private UsageType findFieldUsageType(@NotNull PsiElement element) { + PsiElement originalElement = element; + + while (element != null) { + element = element.getParent(); + + if (element instanceof SmaliInstruction) { + Opcode opcode = ((SmaliInstruction) element).getOpcode(); + if (fieldReadInstructions.contains(opcode)) { + return UsageType.READ; + } else if (fieldWriteInstructions.contains(opcode)) { + return UsageType.WRITE; + } else if (opcode == Opcode.THROW_VERIFICATION_ERROR) { + return VERIFICATION_ERROR; + } + } if (element instanceof SmaliLiteral) { + return LITERAL; + } + } + return UsageType.UNCLASSIFIED; + } } diff --git a/smalidea/src/test/java/org/jf/smalidea/findUsages/FieldUsageTypeTest.java b/smalidea/src/test/java/org/jf/smalidea/findUsages/FieldUsageTypeTest.java new file mode 100644 index 00000000..128c97b5 --- /dev/null +++ b/smalidea/src/test/java/org/jf/smalidea/findUsages/FieldUsageTypeTest.java @@ -0,0 +1,115 @@ +package org.jf.smalidea.findUsages; + +import com.intellij.usages.impl.rules.UsageType; + +public class FieldUsageTypeTest extends UsageTypeTest { + public FieldUsageTypeTest() { + super(new SmaliUsageTypeProvider()); + } + + public void testFieldUsageTypes() throws Exception { + doTest("blah.smali", "" + + ".class public Lblah;\n" + + ".super Ljava/lang/Object;\n" + + "\n" + + ".annotation runtime Lblah;\n" + + " element = Lblah;->blah:Lblah;\n" + + " element2 = .enum Lblah;->blah:Lblah;\n" + + ".end annotation\n" + + "\n" + + ".field public blah:Lblah;\n" + + "\n" + + ".method public blah(Lblah;)Lblah;\n" + + " .registers 2\n" + + "\n" + + " iget v0, v0, Lblah;->blah:Lblah;\n" + + " iget-object v0, v0, Lblah;->blah:Lblah;\n" + + " iget-byte v0, v0, Lblah;->blah:Lblah;\n" + + " iget-char v0, v0, Lblah;->blah:Lblah;\n" + + " iget-object v0, v0, Lblah;->blah:Lblah;\n" + + " iget-object-volatile v0, v0, Lblah;->blah:Lblah;\n" + + " iget-short v0, v0, Lblah;->blah:Lblah;\n" + + " iget-volatile v0, v0, Lblah;->blah:Lblah;\n" + + " iget-wide v0, v0, Lblah;->blah:Lblah;\n" + + " iget-wide-volatile v0, v0, Lblah;->blah:Lblah;\n" + + " sget v0, Lblah;->blah:Lblah;\n" + + " sget-boolean v0, Lblah;->blah:Lblah;\n" + + " sget-byte v0, Lblah;->blah:Lblah;\n" + + " sget-char v0, Lblah;->blah:Lblah;\n" + + " sget-object v0, Lblah;->blah:Lblah;\n" + + " sget-object-volatile v0, Lblah;->blah:Lblah;\n" + + " sget-short v0, Lblah;->blah:Lblah;\n" + + " sget-volatile v0, Lblah;->blah:Lblah;\n" + + " sget-wide v0, Lblah;->blah:Lblah;\n" + + " sget-wide-volatile v0, Lblah;->blah:Lblah;\n" + + " \n" + + " iput v0, v0, Lblah;->blah:Lblah;\n" + + " iput-object v0, v0, Lblah;->blah:Lblah;\n" + + " iput-byte v0, v0, Lblah;->blah:Lblah;\n" + + " iput-char v0, v0, Lblah;->blah:Lblah;\n" + + " iput-object v0, v0, Lblah;->blah:Lblah;\n" + + " iput-object-volatile v0, v0, Lblah;->blah:Lblah;\n" + + " iput-short v0, v0, Lblah;->blah:Lblah;\n" + + " iput-volatile v0, v0, Lblah;->blah:Lblah;\n" + + " iput-wide v0, v0, Lblah;->blah:Lblah;\n" + + " iput-wide-volatile v0, v0, Lblah;->blah:Lblah;\n" + + " sput v0, Lblah;->blah:Lblah;\n" + + " sput-boolean v0, Lblah;->blah:Lblah;\n" + + " sput-byte v0, Lblah;->blah:Lblah;\n" + + " sput-char v0, Lblah;->blah:Lblah;\n" + + " sput-object v0, Lblah;->blah:Lblah;\n" + + " sput-object-volatile v0, Lblah;->blah:Lblah;\n" + + " sput-short v0, Lblah;->blah:Lblah;\n" + + " sput-volatile v0, Lblah;->blah:Lblah;\n" + + " sput-wide v0, Lblah;->blah:Lblah;\n" + + " sput-wide-volatile v0, Lblah;->blah:Lblah;\n" + + "\n" + + " throw-verification-error generic-error, Lblah;->blah:Lblah;\n" + + "\n" + + " return-void\n" + + ".end method\n", + 1, SmaliUsageTypeProvider.LITERAL, + 2, SmaliUsageTypeProvider.LITERAL, + 3, UsageType.READ, + 4, UsageType.READ, + 5, UsageType.READ, + 6, UsageType.READ, + 7, UsageType.READ, + 8, UsageType.READ, + 9, UsageType.READ, + 10, UsageType.READ, + 11, UsageType.READ, + 12, UsageType.READ, + 13, UsageType.READ, + 14, UsageType.READ, + 15, UsageType.READ, + 16, UsageType.READ, + 17, UsageType.READ, + 18, UsageType.READ, + 19, UsageType.READ, + 20, UsageType.READ, + 21, UsageType.READ, + 22, UsageType.READ, + 23, UsageType.WRITE, + 24, UsageType.WRITE, + 25, UsageType.WRITE, + 26, UsageType.WRITE, + 27, UsageType.WRITE, + 28, UsageType.WRITE, + 29, UsageType.WRITE, + 30, UsageType.WRITE, + 31, UsageType.WRITE, + 32, UsageType.WRITE, + 33, UsageType.WRITE, + 34, UsageType.WRITE, + 35, UsageType.WRITE, + 36, UsageType.WRITE, + 37, UsageType.WRITE, + 38, UsageType.WRITE, + 39, UsageType.WRITE, + 40, UsageType.WRITE, + 41, UsageType.WRITE, + 42, UsageType.WRITE, + 43, SmaliUsageTypeProvider.VERIFICATION_ERROR); + } +} diff --git a/smalidea/testData/InvalidEnumLiteral.txt b/smalidea/testData/InvalidEnumLiteral.txt index 2b595727..b2acd87d 100644 --- a/smalidea/testData/InvalidEnumLiteral.txt +++ b/smalidea/testData/InvalidEnumLiteral.txt @@ -24,13 +24,15 @@ smali.FILE PsiElement(LITERAL) PsiElement(ENUM_DIRECTIVE)('.enum') PsiWhiteSpace(' ') - PsiElement(CLASS_TYPE) - PsiElement(CLASS_DESCRIPTOR)('Lblah;') - PsiElement(ARROW)('->') - PsiElement(SIMPLE_NAME)('blah') - PsiWhiteSpace(' ') - PsiErrorElement:mismatched input '.blah' expecting COLON - PsiElement(BAD_CHARACTER)('.blah') - PsiWhiteSpace(' ') - PsiElement(CLASS_TYPE) - PsiElement(CLASS_DESCRIPTOR)('Lblah;') \ No newline at end of file + PsiElement(FIELD_REFERENCE) + PsiElement(CLASS_TYPE) + PsiElement(CLASS_DESCRIPTOR)('Lblah;') + PsiElement(ARROW)('->') + PsiElement(MEMBER_NAME) + PsiElement(SIMPLE_NAME)('blah') + PsiWhiteSpace(' ') + PsiErrorElement:mismatched input '.blah' expecting COLON + PsiElement(BAD_CHARACTER)('.blah') + PsiWhiteSpace(' ') + PsiElement(CLASS_TYPE) + PsiElement(CLASS_DESCRIPTOR)('Lblah;') \ No newline at end of file