From 73d29aa52f22418a98232538fa87258d18114295 Mon Sep 17 00:00:00 2001 From: "JesusFreke@JesusFreke.com" Date: Sun, 17 May 2009 21:24:50 +0000 Subject: [PATCH] - Added support for .implements directive, to declare interfaces that a class implements - Added support for the class level .source directive, to set the source file string for the class - Changed the parser so that the top level directives (.class, .super, .implements, etc.) can appear anywhere in the file in any order, instead of requiring them to be at the front of the file in a specific order - Added some better error reporting to the parser, and changed the lexer to immediately exit on an error git-svn-id: https://smali.googlecode.com/svn/trunk@41 55b6fa8a-2a1e-11de-a435-ffa8d773f76a --- .../antlr3/org/JesusFreke/smali/smaliLexer.g | 42 ++++++----- .../antlr3/org/JesusFreke/smali/smaliParser.g | 70 +++++++++++++++++-- .../org/JesusFreke/smali/smaliTreeWalker.g | 25 ++++++- .../org/JesusFreke/dexlib/ClassDefItem.java | 6 +- src/test/resources/examples/HelloWorld2.smali | 25 +++++++ 5 files changed, 143 insertions(+), 25 deletions(-) diff --git a/src/main/antlr3/org/JesusFreke/smali/smaliLexer.g b/src/main/antlr3/org/JesusFreke/smali/smaliLexer.g index edd15f16..2c0245c6 100644 --- a/src/main/antlr3/org/JesusFreke/smali/smaliLexer.g +++ b/src/main/antlr3/org/JesusFreke/smali/smaliLexer.g @@ -178,23 +178,13 @@ import java.util.ArrayDeque; tokens.add(token); } -/*protected void mismatch(IntStream input, int ttype, BitSet follow) throws RecognitionException -{ - throw new MismatchedTokenException(ttype, input); + public void reportError(RecognitionException e) + { + throw new RuntimeException(e); + } } -public Object recoverFromMismatchedSet(IntStream input, RecognitionException e, BitSet follow) throws RecognitionException -{ - throw e; -}*/ -} - -/*@rulecatch { -catch (RecognitionException e) { -throw e; -} -}*/ CLASS_PHRASE : CLASS_DIRECTIVE_EMIT @@ -207,6 +197,16 @@ SUPER_PHRASE WS CLASS_DESCRIPTOR_EMIT; +IMPLEMENTS_PHRASE + : IMPLEMENTS_DIRECTIVE_EMIT + WS + CLASS_DESCRIPTOR_EMIT; + +SOURCE_PHRASE + : SOURCE_DIRECTIVE_EMIT + WS + STRING_LITERAL_EMIT; + FIELD_PHRASE : FIELD_DIRECTIVE_EMIT WS @@ -461,7 +461,7 @@ CATCH_PHRASE (LABEL_EMIT | OFFSET_EMIT) WS 'to' WS (LABEL_EMIT | OFFSET_EMIT) - WS 'using' + WS 'using' WS (LABEL_EMIT | OFFSET_EMIT); @@ -485,6 +485,16 @@ fragment SUPER_DIRECTIVE_EMIT : SUPER_DIRECTIVE {emit($SUPER_DIRECTIVE, SUPER_DIRECTIVE);}; fragment SUPER_DIRECTIVE : '.super'; + +fragment IMPLEMENTS_DIRECTIVE_EMIT + : IMPLEMENTS_DIRECTIVE {emit($IMPLEMENTS_DIRECTIVE, IMPLEMENTS_DIRECTIVE);}; +fragment IMPLEMENTS_DIRECTIVE + : '.implements'; + +fragment SOURCE_DIRECTIVE_EMIT + : SOURCE_DIRECTIVE {emit($SOURCE_DIRECTIVE, SOURCE_DIRECTIVE);}; +fragment SOURCE_DIRECTIVE + : '.source'; fragment FIELD_DIRECTIVE_EMIT : FIELD_DIRECTIVE {emit($FIELD_DIRECTIVE, FIELD_DIRECTIVE);}; @@ -505,7 +515,7 @@ fragment REGISTERS_DIRECTIVE_EMIT : REGISTERS_DIRECTIVE {emit($REGISTERS_DIRECTIVE, REGISTERS_DIRECTIVE);}; fragment REGISTERS_DIRECTIVE : '.registers'; - + fragment ARRAY_DATA_DIRECTIVE_EMIT : ARRAY_DATA_DIRECTIVE {emit($ARRAY_DATA_DIRECTIVE, ARRAY_DATA_DIRECTIVE);}; fragment ARRAY_DATA_DIRECTIVE diff --git a/src/main/antlr3/org/JesusFreke/smali/smaliParser.g b/src/main/antlr3/org/JesusFreke/smali/smaliParser.g index 00c816c6..aaa4329b 100644 --- a/src/main/antlr3/org/JesusFreke/smali/smaliParser.g +++ b/src/main/antlr3/org/JesusFreke/smali/smaliParser.g @@ -38,6 +38,8 @@ tokens { //I_* tokens are imaginary tokens used as parent AST nodes I_CLASS_DEF; I_SUPER; + I_IMPLEMENTS; + I_SOURCE; I_ACCESS_LIST; I_METHODS; I_FIELDS; @@ -108,21 +110,81 @@ import org.JesusFreke.dexlib.code.Format.*; } -smali_file: header methods_and_fields -> ^(I_CLASS_DEF header methods_and_fields); +@members { + public String getErrorMessage(RecognitionException e, + String[] tokenNames) + { + List stack = getRuleInvocationStack(e, this.getClass().getName()); + String msg = null; + if ( e instanceof NoViableAltException ) { + NoViableAltException nvae = (NoViableAltException)e; + msg = " no viable alt; token="+e.token+ + " (decision="+nvae.decisionNumber+ + " state "+nvae.stateNumber+")"+ + " decision=<<"+nvae.grammarDecisionDescription+">>"; + } + else { + msg = super.getErrorMessage(e, tokenNames); + } + return stack+" "+msg; + } + + public String getTokenErrorDisplay(Token t) { + return t.toString(); + } +} -header : class_spec super_spec; +smali_file + scope + { + boolean hasClassSpec; + boolean hasSuperSpec; + } + : + { + $smali_file::hasClassSpec = false; + $smali_file::hasSuperSpec = false; + } + ( {!$smali_file::hasClassSpec}?=> class_spec {$smali_file::hasClassSpec = true;} + | {!$smali_file::hasSuperSpec}?=> super_spec {$smali_file::hasSuperSpec = true;} + | implements_spec + | source_spec + | method + | field)* + { + if (!$smali_file::hasClassSpec) { + //TODO: throw correct exception type + throw new RuntimeException("The file must contain a .class directive"); + } + + if (!$smali_file::hasSuperSpec) { + //TODO: throw correct exception type + throw new RuntimeException("The file must contain a .super directive"); + } + } + -> ^(I_CLASS_DEF + class_spec + super_spec + implements_spec* + source_spec + ^(I_METHODS method*) ^(I_FIELDS field*)); + class_spec : CLASS_DIRECTIVE access_list CLASS_DESCRIPTOR -> CLASS_DESCRIPTOR access_list; super_spec : SUPER_DIRECTIVE CLASS_DESCRIPTOR -> ^(I_SUPER[$start, "I_SUPER"] CLASS_DESCRIPTOR); +implements_spec + : IMPLEMENTS_DIRECTIVE CLASS_DESCRIPTOR -> ^(I_IMPLEMENTS[$start, "I_IMPLEMENTS"] CLASS_DESCRIPTOR); + +source_spec + : SOURCE_DIRECTIVE STRING_LITERAL -> ^(I_SOURCE[$start, "I_SOURCE"] STRING_LITERAL); + access_list : ACCESS_SPEC+ -> ^(I_ACCESS_LIST[$start,"I_ACCESS_LIST"] ACCESS_SPEC+); -methods_and_fields - : (method | field)* -> ^(I_METHODS method*) ^(I_FIELDS field*); field : FIELD_DIRECTIVE access_list MEMBER_NAME field_type_descriptor literal? -> ^(I_FIELD[$start, "I_FIELD"] MEMBER_NAME access_list ^(I_FIELD_TYPE field_type_descriptor) ^(I_FIELD_INITIAL_VALUE literal)?); diff --git a/src/main/antlr3/org/JesusFreke/smali/smaliTreeWalker.g b/src/main/antlr3/org/JesusFreke/smali/smaliTreeWalker.g index 5ffaa987..d981bf2b 100644 --- a/src/main/antlr3/org/JesusFreke/smali/smaliTreeWalker.g +++ b/src/main/antlr3/org/JesusFreke/smali/smaliTreeWalker.g @@ -90,10 +90,10 @@ import org.JesusFreke.dexlib.code.Format.*; smali_file returns[ClassDefItem classDefItem] : ^(I_CLASS_DEF header methods fields); -header : class_spec super_spec +header : class_spec super_spec implements_list source_spec { classDataItem = new ClassDataItem(dexFile, 0); - classDefItem = new ClassDefItem(dexFile, $class_spec.type, $class_spec.accessFlags, $super_spec.type, classDataItem); + classDefItem = new ClassDefItem(dexFile, $class_spec.type, $class_spec.accessFlags, $super_spec.type, $implements_list.implementsList, $source_spec.source, classDataItem); }; class_spec returns[TypeIdItem type, int accessFlags] @@ -108,6 +108,27 @@ super_spec returns[TypeIdItem type] { $type = $class_type_descriptor.type; }; + + +implements_spec returns[TypeIdItem type] + : ^(I_IMPLEMENTS class_type_descriptor) + { + $type = $class_type_descriptor.type; + }; + +implements_list returns[TypeListItem implementsList] +@init { ArrayList typeList; } + : {typeList = new ArrayList();} + (implements_spec {typeList.add($implements_spec.type);} )* + {if (typeList.size() > 0) $implementsList = new TypeListItem(dexFile, typeList); + else $implementsList = null;}; + +source_spec returns[StringIdItem source] + : {$source = null;} + ^(I_SOURCE string_literal {$source = new StringIdItem(dexFile, $string_literal.value);}) + | ; + + access_list returns [int value] @init diff --git a/src/main/java/org/JesusFreke/dexlib/ClassDefItem.java b/src/main/java/org/JesusFreke/dexlib/ClassDefItem.java index 7bb5b2ec..3ef22945 100644 --- a/src/main/java/org/JesusFreke/dexlib/ClassDefItem.java +++ b/src/main/java/org/JesusFreke/dexlib/ClassDefItem.java @@ -69,7 +69,7 @@ public class ClassDefItem extends IndexedItem { }; } - public ClassDefItem(DexFile dexFile, TypeIdItem classType, int accessFlags, TypeIdItem superType, ClassDataItem classDataItem) { + public ClassDefItem(DexFile dexFile, TypeIdItem classType, int accessFlags, TypeIdItem superType, TypeListItem implementsList, StringIdItem source, ClassDataItem classDataItem) { super(-1); this.dexFile = dexFile; @@ -78,8 +78,8 @@ public class ClassDefItem extends IndexedItem { this.classType = new IndexedItemReference(dexFile, classType, new IntegerField()), this.accessFlags = new IntegerField(accessFlags), superclassType = new IndexedItemReference(dexFile, superType, new IntegerField()), - classInterfacesList = new OffsettedItemReference(dexFile.TypeListsSection, new IntegerField()), - sourceFile = new IndexedItemReference(dexFile.StringIdsSection, new IntegerField()), + classInterfacesList = new OffsettedItemReference(dexFile, implementsList, new IntegerField()), + sourceFile = new IndexedItemReference(dexFile, source, new IntegerField()), classAnnotations = new OffsettedItemReference(dexFile.AnnotationDirectoriesSection, new IntegerField()), classData = new OffsettedItemReference(dexFile, classDataItem, new IntegerField()), staticFieldInitialValues = new OffsettedItemReference(dexFile.EncodedArraysSection, new IntegerField()) diff --git a/src/test/resources/examples/HelloWorld2.smali b/src/test/resources/examples/HelloWorld2.smali index 447c552b..efb8ca49 100644 --- a/src/test/resources/examples/HelloWorld2.smali +++ b/src/test/resources/examples/HelloWorld2.smali @@ -1,6 +1,31 @@ .class public Lorg/JesusFreke/HelloWorld2/HelloWorld2; + .super Landroid/app/Activity; +.source "HelloWorld2.smali" + +;two random interfaces with only a single method to implement +.implements Landroid/util/Printer; +.implements Landroid/accounts/AccountMonitorListener; + + + +.method public println(Ljava/lang/String;)V + .registers 1 + + return-void +.end method + + + + +.method public onAccountsUpdated([Ljava/lang/String;)V + .registers 1 + + return-void +.end method + + .field private helloWorld Ljava/lang/String; .field private static helloWorldStatic Ljava/lang/String;