From 6cbfb8b875521a24566dfb36fd1a0e2019976b74 Mon Sep 17 00:00:00 2001 From: "JesusFreke@JesusFreke.com" Date: Thu, 28 May 2009 00:44:20 +0000 Subject: [PATCH] Added support for annotations git-svn-id: https://smali.googlecode.com/svn/trunk@48 55b6fa8a-2a1e-11de-a435-ffa8d773f76a --- .../antlr3/org/JesusFreke/smali/smaliLexer.g | 164 ++++++++++++---- .../antlr3/org/JesusFreke/smali/smaliParser.g | 95 +++++++--- .../org/JesusFreke/smali/smaliTreeWalker.g | 179 +++++++++++++++--- .../dexlib/AnnotationDirectoryItem.java | 96 +++++++++- .../org/JesusFreke/dexlib/AnnotationItem.java | 10 + .../JesusFreke/dexlib/AnnotationSetItem.java | 13 +- .../dexlib/AnnotationSetRefList.java | 9 + .../org/JesusFreke/dexlib/ClassDefItem.java | 17 +- .../EncodedValue/AnnotationElement.java | 7 + .../AnnotationEncodedValueSubField.java | 8 + .../dexlib/util/DebugInfoBuilder.java | 4 +- 11 files changed, 501 insertions(+), 101 deletions(-) diff --git a/src/main/antlr3/org/JesusFreke/smali/smaliLexer.g b/src/main/antlr3/org/JesusFreke/smali/smaliLexer.g index 04e2bd45..f86f43d0 100644 --- a/src/main/antlr3/org/JesusFreke/smali/smaliLexer.g +++ b/src/main/antlr3/org/JesusFreke/smali/smaliLexer.g @@ -215,7 +215,10 @@ FIELD_PHRASE WS FIELD_TYPE_DESCRIPTOR_EMITCHILD WS? - ('=' WS? LITERAL_EMITCHILD)?; + (EQUAL_EMIT WS? LITERAL_EMITCHILD)?; + +END_FIELD_PHRASE + : END_FIELD_DIRECTIVE_EMIT; METHOD_PHRASE : METHOD_DIRECTIVE_EMIT @@ -239,7 +242,7 @@ INSTRUCTION_FORMAT11n_PHRASE : INSTRUCTION_FORMAT11n_EMIT WS REGISTER_EMIT - WS? ',' WS? + WS? COMMA_EMIT WS? INTEGRAL_LITERAL_EMITCHILD; INSTRUCTION_FORMAT11x_PHRASE @@ -251,7 +254,7 @@ INSTRUCTION_FORMAT12x_PHRASE : INSTRUCTION_FORMAT12x_EMIT WS REGISTER_EMIT - WS? ',' WS? + WS? COMMA_EMIT WS? REGISTER_EMIT; INSTRUCTION_FORMAT20t_PHRASE @@ -263,7 +266,7 @@ INSTRUCTION_FORMAT21c_FIELD_PHRASE : INSTRUCTION_FORMAT21c_FIELD_EMIT WS REGISTER_EMIT - WS? ',' WS? + WS? COMMA_EMIT WS? FULLY_QUALIFIED_MEMBER_NAME_EMITCHILDREN WS FIELD_TYPE_DESCRIPTOR_EMITCHILD; @@ -272,53 +275,53 @@ INSTRUCTION_FORMAT21c_STRING_PHRASE : INSTRUCTION_FORMAT21c_STRING_EMIT WS REGISTER_EMIT - WS? ',' WS? + WS? COMMA_EMIT WS? STRING_LITERAL_EMIT; INSTRUCTION_FORMAT21c_TYPE_PHRASE : INSTRUCTION_FORMAT21c_TYPE_EMIT WS REGISTER_EMIT - WS? ',' WS? + WS? COMMA_EMIT WS? CLASS_OR_ARRAY_TYPE_DESCRIPTOR_EMITCHILD; INSTRUCTION_FORMAT21h_PHRASE : INSTRUCTION_FORMAT21h_EMIT WS REGISTER_EMIT - WS? ',' WS? + WS? COMMA_EMIT WS? INTEGRAL_LITERAL_EMITCHILD; INSTRUCTION_FORMAT21s_PHRASE : INSTRUCTION_FORMAT21s_EMIT WS REGISTER_EMIT - WS? ',' WS? + WS? COMMA_EMIT WS? INTEGRAL_LITERAL_EMITCHILD; INSTRUCTION_FORMAT21t_PHRASE : INSTRUCTION_FORMAT21t_EMIT WS REGISTER_EMIT - WS? ',' WS? + WS? COMMA_EMIT WS? (LABEL_EMIT | OFFSET_EMIT); INSTRUCTION_FORMAT22b_PHRASE : INSTRUCTION_FORMAT22b_EMIT WS REGISTER_EMIT - WS? ',' WS? + WS? COMMA_EMIT WS? REGISTER_EMIT - WS? ',' WS? + WS? COMMA_EMIT WS? INTEGRAL_LITERAL_EMITCHILD; INSTRUCTION_FORMAT22c_FIELD_PHRASE : INSTRUCTION_FORMAT22c_FIELD_EMIT WS REGISTER_EMIT - WS? ',' WS? + WS? COMMA_EMIT WS? REGISTER_EMIT - WS? ',' WS? + WS? COMMA_EMIT WS? FULLY_QUALIFIED_MEMBER_NAME_EMITCHILDREN WS FIELD_TYPE_DESCRIPTOR_EMITCHILD; @@ -327,43 +330,43 @@ INSTRUCTION_FORMAT22c_TYPE_PHRASE : INSTRUCTION_FORMAT22c_TYPE_EMIT WS REGISTER_EMIT - WS? ',' WS? + WS? COMMA_EMIT WS? REGISTER_EMIT - WS? ',' WS? + WS? COMMA_EMIT WS? FIELD_TYPE_DESCRIPTOR_EMITCHILD; INSTRUCTION_FORMAT22s_PHRASE : INSTRUCTION_FORMAT22s_EMIT WS REGISTER_EMIT - WS? ',' WS? + WS? COMMA_EMIT WS? REGISTER_EMIT - WS? ',' WS? + WS? COMMA_EMIT WS? INTEGRAL_LITERAL_EMITCHILD; INSTRUCTION_FORMAT22t_PHRASE : INSTRUCTION_FORMAT22t_EMIT WS REGISTER_EMIT - WS? ',' WS? + WS? COMMA_EMIT WS? REGISTER_EMIT - WS? ',' WS? + WS? COMMA_EMIT WS? (LABEL_EMIT | OFFSET_EMIT); INSTRUCTION_FORMAT22x_PHRASE : INSTRUCTION_FORMAT22x_EMIT WS REGISTER_EMIT - WS? ',' WS? + WS? COMMA_EMIT WS? REGISTER_EMIT; INSTRUCTION_FORMAT23x_PHRASE : INSTRUCTION_FORMAT23x_EMIT WS REGISTER_EMIT - WS? ',' WS? + WS? COMMA_EMIT WS? REGISTER_EMIT - WS? ',' WS? + WS? COMMA_EMIT WS? REGISTER_EMIT; INSTRUCTION_FORMAT30t_PHRASE @@ -375,35 +378,35 @@ INSTRUCTION_FORMAT31c_PHRASE : INSTRUCTION_FORMAT31c_EMIT WS REGISTER_EMIT - WS? ',' WS? + WS? COMMA_EMIT WS? STRING_LITERAL_EMIT; INSTRUCTION_FORMAT31i_PHRASE : INSTRUCTION_FORMAT31i_EMIT WS REGISTER_EMIT - WS? ',' WS? + WS? COMMA_EMIT WS? (FIXED_32BIT_LITERAL_EMITCHILD); INSTRUCTION_FORMAT31t_PHRASE : INSTRUCTION_FORMAT31t_EMIT WS REGISTER_EMIT - WS? ',' WS? + WS? COMMA_EMIT WS? (LABEL_EMIT | OFFSET_EMIT); INSTRUCTION_FORMAT32x_PHRASE : INSTRUCTION_FORMAT32x_EMIT WS REGISTER_EMIT - WS? ',' WS? + WS? COMMA_EMIT WS? REGISTER_EMIT; INSTRUCTION_FORMAT35c_METHOD_PHRASE : INSTRUCTION_FORMAT35c_METHOD_EMIT WS REGISTER_LIST_EMITCHILDREN - WS? ',' WS? + WS? COMMA_EMIT WS? FULLY_QUALIFIED_MEMBER_NAME_EMITCHILDREN METHOD_PROTOTYPE_EMITCHILDREN; @@ -411,7 +414,7 @@ INSTRUCTION_FORMAT3rc_METHOD_PHRASE : INSTRUCTION_FORMAT3rc_METHOD_EMIT WS REGISTER_RANGE_EMITCHILDREN - WS? ',' WS? + WS? COMMA_EMIT WS? FULLY_QUALIFIED_MEMBER_NAME_EMITCHILDREN METHOD_PROTOTYPE_EMITCHILDREN; @@ -419,7 +422,7 @@ INSTRUCTION_FORMAT51l_PHRASE : INSTRUCTION_FORMAT51l_EMIT WS REGISTER_EMIT - WS? ',' WS? + WS? COMMA_EMIT WS? (FIXED_LITERAL_EMITCHILD); ARRAY_DATA_PHRASE @@ -473,16 +476,19 @@ PARAMETER_PHRASE : PARAMETER_DIRECTIVE_EMIT (WS STRING_LITERAL_EMIT?)?; +END_PARAMETER_PHRASE + : END_PARAMETER_DIRECTIVE_EMIT; + LOCAL_PHRASE : LOCAL_DIRECTIVE_EMIT WS REGISTER_EMIT - WS? ',' WS? + WS? COMMA_EMIT WS? SIMPLE_NAME_EMIT WS FIELD_TYPE_DESCRIPTOR_EMITCHILD WS? - ( ',' WS? STRING_LITERAL_EMIT)?; + ( COMMA_EMIT WS? STRING_LITERAL_EMIT)?; END_LOCAL_PHRASE : END_LOCAL_DIRECTIVE_EMIT @@ -500,6 +506,16 @@ PROLOGUE_PHRASE EPILOGUE_PHRASE : EPILOGUE_DIRECTIVE_EMIT; +ANNOTATION_PHRASE + : ANNOTATION_START_EMIT + WS + ANNOTATION_VISIBILITY_EMIT + WS + CLASS_DESCRIPTOR_EMIT + WS + (ANNOTATION_ELEMENT_EMITCHILDREN WS)* + ANNOTATION_END_EMIT; + //TODO: add support for both relative and absolute offsets? fragment OFFSET_EMIT : OFFSET {emit($OFFSET, OFFSET);}; @@ -536,6 +552,11 @@ fragment FIELD_DIRECTIVE_EMIT fragment FIELD_DIRECTIVE : '.field'; +fragment END_FIELD_DIRECTIVE_EMIT + : END_FIELD_DIRECTIVE {emit($END_FIELD_DIRECTIVE, END_FIELD_DIRECTIVE);}; +fragment END_FIELD_DIRECTIVE + : '.end field'; + fragment METHOD_DIRECTIVE_EMIT : METHOD_DIRECTIVE {emit($METHOD_DIRECTIVE, METHOD_DIRECTIVE);}; fragment METHOD_DIRECTIVE @@ -596,6 +617,11 @@ fragment PARAMETER_DIRECTIVE_EMIT fragment PARAMETER_DIRECTIVE : '.parameter'; +fragment END_PARAMETER_DIRECTIVE_EMIT + : END_PARAMETER_DIRECTIVE {emit($END_PARAMETER_DIRECTIVE, END_PARAMETER_DIRECTIVE);}; +fragment END_PARAMETER_DIRECTIVE + : '.end parameter'; + fragment LOCAL_DIRECTIVE_EMIT : LOCAL_DIRECTIVE {emit($LOCAL_DIRECTIVE, LOCAL_DIRECTIVE);}; fragment LOCAL_DIRECTIVE @@ -629,7 +655,7 @@ fragment REGISTER fragment REGISTER_LIST_EMITCHILDREN : OPEN_BRACKET_EMIT ( WS? - REGISTER_EMIT (WS? ',' WS? REGISTER_EMIT)* + REGISTER_EMIT (WS? COMMA_EMIT WS? REGISTER_EMIT)* WS? | WS?) CLOSE_BRACKET_EMIT; @@ -821,7 +847,69 @@ fragment LITERAL_EMITCHILD | FLOAT_LITERAL_EMIT | DOUBLE_LITERAL_EMIT | CHAR_LITERAL_EMIT - | BOOL_LITERAL_EMIT; + | BOOL_LITERAL_EMIT + | TYPE_DESCRIPTOR_EMITCHILD + | ARRAY_LITERAL_EMITCHILDREN + | SUBANNOTATION_EMITCHILDREN; + +fragment SUBANNOTATION_EMITCHILDREN + : SUBANNOTATION_START_EMIT + WS + CLASS_DESCRIPTOR_EMIT + WS + (ANNOTATION_ELEMENT_EMITCHILDREN WS)* + SUBANNOTATION_END_EMIT + ; + +fragment SUBANNOTATION_START_EMIT + : SUBANNOTATION_START {emit($SUBANNOTATION_START, SUBANNOTATION_START);}; +fragment SUBANNOTATION_START + : '.subannotation'; + +fragment SUBANNOTATION_END_EMIT + : SUBANNOTATION_END {emit($SUBANNOTATION_END, SUBANNOTATION_END);}; +fragment SUBANNOTATION_END + : '.end subannotation'; + +fragment ANNOTATION_START_EMIT + : ANNOTATION_START {emit($ANNOTATION_START, ANNOTATION_START);}; +fragment ANNOTATION_START + : '.annotation'; + +fragment ANNOTATION_END_EMIT + : ANNOTATION_END {emit($ANNOTATION_END, ANNOTATION_END);}; +fragment ANNOTATION_END + : '.end annotation'; + +fragment ANNOTATION_VISIBILITY_EMIT + : ANNOTATION_VISIBILITY {emit($ANNOTATION_VISIBILITY, ANNOTATION_VISIBILITY);}; +fragment ANNOTATION_VISIBILITY + : 'build' + | 'runtime' + | 'system'; + +fragment ANNOTATION_ELEMENT_EMITCHILDREN + : MEMBER_NAME_EMIT + WS? + EQUAL_EMIT + WS? + LITERAL_EMITCHILD; + +fragment ARRAY_LITERAL_EMITCHILDREN + : ARRAY_START_EMIT + WS? + (LITERAL_EMITCHILD WS? (COMMA_EMIT WS? LITERAL_EMITCHILD WS?)*)? + ARRAY_END_EMIT; + +fragment ARRAY_START_EMIT + : ARRAY_START {emit($ARRAY_START, ARRAY_START);}; +fragment ARRAY_START + : '{'; + +fragment ARRAY_END_EMIT + : ARRAY_END {emit($ARRAY_END, ARRAY_END);}; +fragment ARRAY_END + : '}'; fragment STRING_LITERAL_EMIT @@ -1316,3 +1404,13 @@ LINE_COMMENT : (';' ~('\n'|'\r')* ('\r\n' | '\r' | '\n') | ';' ~('\n'|'\r')*) {$channel = HIDDEN;}; + +fragment EQUAL_EMIT + : EQUAL {emit($EQUAL, EQUAL, Token.HIDDEN_CHANNEL);}; +fragment EQUAL + : '='; + +fragment COMMA_EMIT + : COMMA {emit($COMMA, COMMA, Token.HIDDEN_CHANNEL);}; +fragment COMMA + : ','; diff --git a/src/main/antlr3/org/JesusFreke/smali/smaliParser.g b/src/main/antlr3/org/JesusFreke/smali/smaliParser.g index 05021687..57b3856f 100644 --- a/src/main/antlr3/org/JesusFreke/smali/smaliParser.g +++ b/src/main/antlr3/org/JesusFreke/smali/smaliParser.g @@ -52,6 +52,11 @@ tokens { I_REGISTERS; I_LABELS; I_LABEL; + I_ANNOTATIONS; + I_ANNOTATION; + I_ANNOTATION_ELEMENT; + I_SUBANNOTATION; + I_ENCODED_ARRAY; I_ARRAY_ELEMENT_SIZE; I_ARRAY_ELEMENTS; I_PACKED_SWITCH_START_KEY; @@ -146,25 +151,28 @@ import org.JesusFreke.dexlib.code.Format.*; smali_file - : + scope { - boolean hasClassSpec = false; - boolean hasSuperSpec = false; - boolean hasSourceSpec = false; + boolean hasClassSpec; + boolean hasSuperSpec; + boolean hasSourceSpec; } - ( {!hasClassSpec}?=> class_spec {hasClassSpec = true;} - | {!hasSuperSpec}?=> super_spec {hasSuperSpec = true;} + @init { $smali_file::hasClassSpec = $smali_file::hasSuperSpec = $smali_file::hasSourceSpec = false; } + : + ( {!$smali_file::hasClassSpec}?=> class_spec {$smali_file::hasClassSpec = true;} + | {!$smali_file::hasSuperSpec}?=> super_spec {$smali_file::hasSuperSpec = true;} | implements_spec - | {!hasSourceSpec}?=> source_spec {hasSourceSpec = true;} + | {!$smali_file::hasSourceSpec}?=> source_spec {$smali_file::hasSourceSpec = true;} | method - | field)* + | field + | annotation)* { - if (!hasClassSpec) { + if (!$smali_file::hasClassSpec) { //TODO: throw correct exception type throw new RuntimeException("The file must contain a .class directive"); } - if (!hasSuperSpec) { + if (!$smali_file::hasSuperSpec) { //TODO: throw correct exception type throw new RuntimeException("The file must contain a .super directive"); } @@ -174,7 +182,7 @@ smali_file super_spec implements_spec* source_spec - ^(I_METHODS method*) ^(I_FIELDS field*)); + ^(I_METHODS method*) ^(I_FIELDS field*) ^(I_ANNOTATIONS annotation*)); class_spec : CLASS_DIRECTIVE access_list CLASS_DESCRIPTOR -> CLASS_DESCRIPTOR access_list; @@ -192,8 +200,11 @@ access_list : ACCESS_SPEC+ -> ^(I_ACCESS_LIST[$start,"I_ACCESS_LIST"] ACCESS_SPEC+); -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)?); +field : FIELD_DIRECTIVE access_list MEMBER_NAME field_type_descriptor literal? + ( (annotation+ END_FIELD_DIRECTIVE)=> annotation+ END_FIELD_DIRECTIVE + | END_FIELD_DIRECTIVE? + ) + -> ^(I_FIELD[$start, "I_FIELD"] MEMBER_NAME access_list ^(I_FIELD_TYPE field_type_descriptor) ^(I_FIELD_INITIAL_VALUE literal)? ^(I_ANNOTATIONS annotation*)); method scope {int currentAddress;} @@ -215,19 +226,21 @@ fully_qualified_field : CLASS_NAME MEMBER_NAME field_type_descriptor; statements_and_directives + scope {boolean hasRegistersDirective;} : { $method::currentAddress = 0; - boolean hasRegistersDirective = false; + $statements_and_directives::hasRegistersDirective = false; } ( instruction {$method::currentAddress += $instruction.size/2;} - | {!hasRegistersDirective}?=> registers_directive {hasRegistersDirective = true;} + | {!$statements_and_directives::hasRegistersDirective}?=> registers_directive {$statements_and_directives::hasRegistersDirective = true;} | label | catch_directive | parameter_directive | ordered_debug_directive + | annotation )* { - if (!hasRegistersDirective) { + if (!$statements_and_directives::hasRegistersDirective) { //TODO: throw correct exception type here throw new RuntimeException("This method has no register directive"); } @@ -237,7 +250,8 @@ statements_and_directives ^(I_STATEMENTS instruction*) ^(I_CATCHES catch_directive*) ^(I_PARAMETERS parameter_directive*) - ^(I_ORDERED_DEBUG_DIRECTIVES ordered_debug_directive*); + ^(I_ORDERED_DEBUG_DIRECTIVES ordered_debug_directive*) + ^(I_ANNOTATIONS annotation*); registers_directive : REGISTERS_DIRECTIVE integral_literal @@ -250,9 +264,17 @@ catch_directive parameter_directive - : PARAMETER_DIRECTIVE ( STRING_LITERAL -> ^(I_PARAMETER STRING_LITERAL?) - | -> ^(I_PARAMETER I_PARAMETER_NOT_SPECIFIED) - ); + : PARAMETER_DIRECTIVE + ( STRING_LITERAL + ( (annotation+ END_PARAMETER_DIRECTIVE)=> annotation+ END_PARAMETER_DIRECTIVE + | END_PARAMETER_DIRECTIVE? + ) + -> ^(I_PARAMETER STRING_LITERAL ^(I_ANNOTATIONS annotation*)) + | ( (annotation+ END_PARAMETER_DIRECTIVE)=> annotation+ END_PARAMETER_DIRECTIVE + | END_PARAMETER_DIRECTIVE? + ) + -> ^(I_PARAMETER I_PARAMETER_NOT_SPECIFIED ^(I_ANNOTATIONS annotation*)) + ); ordered_debug_directive : line_directive @@ -295,7 +317,7 @@ label : LABEL -> ^(I_LABEL LABEL I_ADDRESS[$start, Integer.toString($method::currentAddress)]); instruction returns [int size] - @init {boolean needsNop = false;} + @init {boolean needsNop = false; int targetCount = 0;} : //e.g. goto endloop: //e.g. goto +3 INSTRUCTION_FORMAT10t (LABEL | OFFSET) {$size = Format10t.Format.getByteCount();} @@ -400,7 +422,7 @@ instruction returns [int size] | PACKED_SWITCH_DIRECTIVE { - int targetCount = 0; + targetCount = 0; if (($method::currentAddress \% 2) != 0) { needsNop = true; $size = 2; @@ -434,7 +456,7 @@ instruction returns [int size] | SPARSE_SWITCH_DIRECTIVE { - int targetCount = 0; + targetCount = 0; if (($method::currentAddress \% 2) != 0) { needsNop = true; $size = 2; @@ -516,10 +538,33 @@ fixed_literal returns[int size] | CHAR_LITERAL {$size = 2;} | BOOL_LITERAL {$size = 1;}; -literal : INTEGER_LITERAL +literal + : INTEGER_LITERAL | LONG_LITERAL + | SHORT_LITERAL_EMIT + | BYTE_LITERAL_EMIT | FLOAT_LITERAL | DOUBLE_LITERAL | CHAR_LITERAL | STRING_LITERAL - | BOOL_LITERAL; + | BOOL_LITERAL + | type_descriptor + | array_literal + | subannotation; + +array_literal + : ARRAY_START literal* ARRAY_END + -> ^(I_ENCODED_ARRAY[$start, "I_ENCODED_ARRAY"] literal*); + +annotation + : ANNOTATION_START ANNOTATION_VISIBILITY CLASS_DESCRIPTOR + annotation_element* ANNOTATION_END + -> ^(I_ANNOTATION[$start, "I_ANNOTATION"] ANNOTATION_VISIBILITY ^(I_SUBANNOTATION[$start, "I_SUBANNOTATION"] CLASS_DESCRIPTOR annotation_element*)); + +annotation_element + : MEMBER_NAME literal + -> ^(I_ANNOTATION_ELEMENT[$start, "I_ANNOTATION_ELEMENT"] MEMBER_NAME literal); + +subannotation + : SUBANNOTATION_START CLASS_DESCRIPTOR annotation_element* SUBANNOTATION_END + -> ^(I_SUBANNOTATION[$start, "I_SUBANNOTATION"] CLASS_DESCRIPTOR annotation_element*); diff --git a/src/main/antlr3/org/JesusFreke/smali/smaliTreeWalker.g b/src/main/antlr3/org/JesusFreke/smali/smaliTreeWalker.g index 520e9e59..4884a920 100644 --- a/src/main/antlr3/org/JesusFreke/smali/smaliTreeWalker.g +++ b/src/main/antlr3/org/JesusFreke/smali/smaliTreeWalker.g @@ -87,13 +87,33 @@ import org.JesusFreke.dexlib.code.Format.*; -smali_file returns[ClassDefItem classDefItem] - : ^(I_CLASS_DEF header methods fields); +smali_file + : ^(I_CLASS_DEF header methods fields annotations) + { + AnnotationDirectoryItem annotationDirectoryItem = null; + + if ( $methods.methodAnnotationSets != null || + $methods.parameterAnnotationSets != null || + $fields.fieldAnnotationSets != null || + $annotations.annotationSetItem != null) { + annotationDirectoryItem = new AnnotationDirectoryItem( + dexFile, + $annotations.annotationSetItem, + $fields.fieldAnnotationSets, + $methods.methodAnnotationSets, + $methods.parameterAnnotationSets); + } + + classDefItem.setAnnotations(annotationDirectoryItem); + }; -header : class_spec super_spec implements_list source_spec + +header returns[TypeIdItem classType, int accessFlags, TypeIdItem superType, TypeListItem implementsList, StringIdItem sourceSpec] +: 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, $implements_list.implementsList, $source_spec.source, 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] @@ -143,20 +163,41 @@ access_list returns [int value] } )+); -fields : ^(I_FIELDS +fields returns[List fieldAnnotationSets] + : ^(I_FIELDS (field { classDefItem.addField($field.encodedField, $field.encodedValue); + if ($field.fieldAnnotationSet != null) { + if ($fieldAnnotationSets == null) { + $fieldAnnotationSets = new ArrayList(); + } + fieldAnnotationSets.add($field.fieldAnnotationSet); + } })*); -methods : ^(I_METHODS +methods returns[List methodAnnotationSets, + List parameterAnnotationSets] + : ^(I_METHODS (method { classDataItem.addMethod($method.encodedMethod); + if ($method.methodAnnotationSet != null) { + if ($methodAnnotationSets == null) { + $methodAnnotationSets = new ArrayList(); + } + $methodAnnotationSets.add($method.methodAnnotationSet); + } + if ($method.parameterAnnotationSets != null) { + if ($parameterAnnotationSets == null) { + $parameterAnnotationSets = new ArrayList(); + } + $parameterAnnotationSets.add($method.parameterAnnotationSets); + } })*); -field returns[ClassDataItem.EncodedField encodedField, EncodedValue encodedValue] - :^(I_FIELD MEMBER_NAME access_list ^(I_FIELD_TYPE field_type_descriptor) field_initial_value) +field returns[ClassDataItem.EncodedField encodedField, EncodedValue encodedValue, AnnotationDirectoryItem.FieldAnnotation fieldAnnotationSet] + :^(I_FIELD MEMBER_NAME access_list ^(I_FIELD_TYPE field_type_descriptor) field_initial_value annotations?) { TypeIdItem classType = classDefItem.getClassType(); StringIdItem memberName = new StringIdItem(dexFile, $MEMBER_NAME.text); @@ -175,23 +216,31 @@ field returns[ClassDataItem.EncodedField encodedField, EncodedValue encodedValue } else { $encodedValue = null; } + + if ($annotations.annotationSetItem != null) { + $fieldAnnotationSet = new AnnotationDirectoryItem.FieldAnnotation(dexFile, fieldIdItem, $annotations.annotationSetItem); + } }; -//TODO: what about type or array encoded values? field_initial_value returns[EncodedValue encodedValue] - : ^(I_FIELD_INITIAL_VALUE - ( integer_literal { $encodedValue = new EncodedValue(dexFile, new IntEncodedValueSubField($integer_literal.value)); } - | long_literal { $encodedValue = new EncodedValue(dexFile, new LongEncodedValueSubField($long_literal.value)); } - | short_literal { $encodedValue = new EncodedValue(dexFile, new ShortEncodedValueSubField($short_literal.value)); } - | byte_literal { $encodedValue = new EncodedValue(dexFile, new ByteEncodedValueSubField($byte_literal.value)); } - | float_literal { $encodedValue = new EncodedValue(dexFile, new FloatEncodedValueSubField($float_literal.value)); } - | double_literal { $encodedValue = new EncodedValue(dexFile, new DoubleEncodedValueSubField($double_literal.value)); } - | char_literal { $encodedValue = new EncodedValue(dexFile, new CharEncodedValueSubField($char_literal.value)); } - | string_literal { $encodedValue = new EncodedValue(dexFile, new EncodedIndexedItemReference(dexFile, new StringIdItem(dexFile, $string_literal.value))); } - | bool_literal { $encodedValue = new EncodedValue(dexFile, new BoolEncodedValueSubField($bool_literal.value)); } - )) - | ; + : ^(I_FIELD_INITIAL_VALUE literal) {$encodedValue = $literal.encodedValue;} + | ; + +literal returns[EncodedValue encodedValue] + : integer_literal { $encodedValue = new EncodedValue(dexFile, new IntEncodedValueSubField($integer_literal.value)); } + | long_literal { $encodedValue = new EncodedValue(dexFile, new LongEncodedValueSubField($long_literal.value)); } + | short_literal { $encodedValue = new EncodedValue(dexFile, new ShortEncodedValueSubField($short_literal.value)); } + | byte_literal { $encodedValue = new EncodedValue(dexFile, new ByteEncodedValueSubField($byte_literal.value)); } + | float_literal { $encodedValue = new EncodedValue(dexFile, new FloatEncodedValueSubField($float_literal.value)); } + | double_literal { $encodedValue = new EncodedValue(dexFile, new DoubleEncodedValueSubField($double_literal.value)); } + | char_literal { $encodedValue = new EncodedValue(dexFile, new CharEncodedValueSubField($char_literal.value)); } + | string_literal { $encodedValue = new EncodedValue(dexFile, new EncodedIndexedItemReference(dexFile, new StringIdItem(dexFile, $string_literal.value))); } + | bool_literal { $encodedValue = new EncodedValue(dexFile, new BoolEncodedValueSubField($bool_literal.value)); } + | type_descriptor { $encodedValue = new EncodedValue(dexFile, new EncodedIndexedItemReference(dexFile, $type_descriptor.type)); } + | array_literal { $encodedValue = new EncodedValue(dexFile, new ArrayEncodedValueSubField(dexFile, $array_literal.values)); } + | subannotation { $encodedValue = new EncodedValue(dexFile, $subannotation.value); }; + //everything but string fixed_size_literal returns[byte[\] value] @@ -280,7 +329,9 @@ sparse_switch_targets[int baseOffset, int targetCount] returns[int[\] targets] })* ); -method returns[ClassDataItem.EncodedMethod encodedMethod] +method returns[ ClassDataItem.EncodedMethod encodedMethod, + AnnotationDirectoryItem.MethodAnnotation methodAnnotationSet, + AnnotationDirectoryItem.ParameterAnnotation parameterAnnotationSets] scope { HashMap labels; @@ -303,6 +354,7 @@ method returns[ClassDataItem.EncodedMethod encodedMethod] catches parameters ordered_debug_directives + annotations ) { MethodIdItem methodIdItem = $method_name_and_prototype.methodIdItem; @@ -345,6 +397,14 @@ method returns[ClassDataItem.EncodedMethod encodedMethod] handlers); $encodedMethod = new ClassDataItem.EncodedMethod(dexFile, methodIdItem, access, codeItem); + + if ($annotations.annotationSetItem != null) { + $methodAnnotationSet = new AnnotationDirectoryItem.MethodAnnotation(dexFile, methodIdItem, $annotations.annotationSetItem); + } + + if ($parameters.parameterAnnotations != null) { + $parameterAnnotationSets = new AnnotationDirectoryItem.ParameterAnnotation(dexFile, methodIdItem, $parameters.parameterAnnotations); + } }; method_prototype returns[ProtoIdItem protoIdItem] @@ -436,14 +496,39 @@ address returns[int address] $address = Integer.parseInt($I_ADDRESS.text); }; -parameters - : ^(I_PARAMETERS parameter*); +parameters returns[AnnotationSetRefList parameterAnnotations] + @init + { + int parameterCount = 0; + List annotationSetItems = new ArrayList(); + } + : ^(I_PARAMETERS (parameter + { + if ($parameter.parameterAnnotationSet != null) { + while (annotationSetItems.size() < parameterCount) { + annotationSetItems.add(new AnnotationSetItem(dexFile, -1)); + } + annotationSetItems.add($parameter.parameterAnnotationSet); + } + + parameterCount++; + })* + ) + { + if (annotationSetItems.size() > 0) { + while (annotationSetItems.size() < parameterCount) { + annotationSetItems.add(new AnnotationSetItem(dexFile, -1)); + } + $parameterAnnotations = new AnnotationSetRefList(dexFile, annotationSetItems); + } + }; -parameter - : ^(I_PARAMETER ( - string_literal {$method::debugInfo.addParameterName($string_literal.value);} +parameter returns[AnnotationSetItem parameterAnnotationSet] + : ^(I_PARAMETER ( string_literal {$method::debugInfo.addParameterName($string_literal.value);} | I_PARAMETER_NOT_SPECIFIED {$method::debugInfo.addParameterName(null);} - )); + ) + annotations {$parameterAnnotationSet = $annotations.annotationSetItem;} + ); ordered_debug_directives : ^(I_ORDERED_DEBUG_DIRECTIVES ( line @@ -998,3 +1083,41 @@ string_literal returns[String value] bool_literal returns[boolean value] : BOOL_LITERAL { $value = Boolean.parseBoolean($BOOL_LITERAL.text); }; + +array_literal returns[ArrayList values] + : {$values = new ArrayList();} + ^(I_ENCODED_ARRAY (literal {$values.add($literal.encodedValue);})*); + + +annotations returns[AnnotationSetItem annotationSetItem] + : {ArrayList annotationList = new ArrayList();} + ^(I_ANNOTATIONS (annotation {annotationList.add($annotation.annotationItem);} )*) + { + if (annotationList.size() > 0) { + $annotationSetItem = new AnnotationSetItem(dexFile, annotationList); + } + }; + + +annotation returns[AnnotationItem annotationItem] + : ^(I_ANNOTATION ANNOTATION_VISIBILITY subannotation) + { + AnnotationVisibility visibility = AnnotationVisibility.fromName($ANNOTATION_VISIBILITY.text); + $annotationItem = new AnnotationItem(dexFile, visibility, $subannotation.value); + }; + +annotation_element returns[AnnotationElement element] + : ^(I_ANNOTATION_ELEMENT MEMBER_NAME literal) + { + $element = new AnnotationElement(dexFile, new StringIdItem(dexFile, $MEMBER_NAME.text), $literal.encodedValue); + }; + +subannotation returns[AnnotationEncodedValueSubField value] + : {ArrayList elements = new ArrayList();} + ^( I_SUBANNOTATION + class_type_descriptor + (annotation_element {elements.add($annotation_element.element);} )* ) + { + $value = new AnnotationEncodedValueSubField(dexFile, $class_type_descriptor.type, elements); + }; + diff --git a/src/main/java/org/JesusFreke/dexlib/AnnotationDirectoryItem.java b/src/main/java/org/JesusFreke/dexlib/AnnotationDirectoryItem.java index 02145036..ece7ab12 100644 --- a/src/main/java/org/JesusFreke/dexlib/AnnotationDirectoryItem.java +++ b/src/main/java/org/JesusFreke/dexlib/AnnotationDirectoryItem.java @@ -31,6 +31,8 @@ package org.JesusFreke.dexlib; import org.JesusFreke.dexlib.ItemType; import java.util.ArrayList; +import java.util.List; +import java.util.Collections; //TODO: fix field names in dex-format.html and submit public class AnnotationDirectoryItem extends OffsettedItem { @@ -74,6 +76,39 @@ public class AnnotationDirectoryItem extends OffsettedItem fieldAnnotations, + List methodAnnotations, + List parameterAnnotations) { + this(dexFile, -1); + + this.classAnnotations.setReference(classAnnotations); + + if (fieldAnnotations != null) { + this.fieldAnnotationList.addAll(fieldAnnotations); + } + + if (methodAnnotations != null) { + this.methodAnnotationList.addAll(methodAnnotations); + } + + if (parameterAnnotations != null) { + this.parameterAnnotationList.addAll(parameterAnnotations); + } + } + + @Override + public int place(int index, int offset) + { + Collections.sort(fieldAnnotationList); + Collections.sort(methodAnnotationList); + Collections.sort(parameterAnnotationList); + return super.place(index, offset); + } + protected int getAlignment() { return 4; } @@ -86,48 +121,91 @@ public class AnnotationDirectoryItem extends OffsettedItem { + public static class FieldAnnotation extends CompositeField + implements Comparable { private final Field[] fields; + private final IndexedItemReference field; + private final OffsettedItemReference annotationSet; + public FieldAnnotation(DexFile dexFile) { fields = new Field[] { - new IndexedItemReference(dexFile.FieldIdsSection, new IntegerField()), - new OffsettedItemReference(dexFile.AnnotationSetsSection, new IntegerField()) + field = new IndexedItemReference(dexFile.FieldIdsSection, new IntegerField()), + annotationSet = new OffsettedItemReference(dexFile.AnnotationSetsSection, new IntegerField()) }; } + public FieldAnnotation(DexFile dexFile, FieldIdItem field, AnnotationSetItem annotationSet) { + this(dexFile); + this.field.setReference(field); + this.annotationSet.setReference(annotationSet); + } + protected Field[] getFields() { return fields; } + + public int compareTo(FieldAnnotation o) { + return ((Integer)field.getReference().getIndex()).compareTo(o.field.getReference().getIndex()); + } } - public class MethodAnnotation extends CompositeField { + public static class MethodAnnotation extends CompositeField + implements Comparable { private final Field[] fields; + private final IndexedItemReference method; + private final OffsettedItemReference annotationSet; + public MethodAnnotation(DexFile dexFile) { fields = new Field[] { - new IndexedItemReference(dexFile.MethodIdsSection, new IntegerField()), - new OffsettedItemReference(dexFile.AnnotationSetsSection, new IntegerField()) + method = new IndexedItemReference(dexFile.MethodIdsSection, new IntegerField()), + annotationSet = new OffsettedItemReference(dexFile.AnnotationSetsSection, new IntegerField()) }; } + public MethodAnnotation(DexFile dexFile, MethodIdItem method, AnnotationSetItem annotationSet) { + this(dexFile); + this.method.setReference(method); + this.annotationSet.setReference(annotationSet); + } + protected Field[] getFields() { return fields; } + + public int compareTo(MethodAnnotation o) { + return ((Integer)method.getReference().getIndex()).compareTo(o.method.getReference().getIndex()); + } } - public class ParameterAnnotation extends CompositeField { + public static class ParameterAnnotation extends CompositeField + implements Comparable { private final Field[] fields; + private final IndexedItemReference method; + private final OffsettedItemReference parameterAnnotations; + public ParameterAnnotation(DexFile dexFile) { fields = new Field[] { - new IndexedItemReference(dexFile.MethodIdsSection, new IntegerField()), - new OffsettedItemReference(dexFile.AnnotationSetsSection, new IntegerField()) + method = new IndexedItemReference(dexFile.MethodIdsSection, new IntegerField()), + parameterAnnotations = new OffsettedItemReference( + dexFile.AnnotationSetRefListsSection, new IntegerField()) }; } + public ParameterAnnotation(DexFile dexFile, MethodIdItem method, AnnotationSetRefList parameterAnnotations) { + this(dexFile); + this.method.setReference(method); + this.parameterAnnotations.setReference(parameterAnnotations); + } + protected Field[] getFields() { return fields; } + + public int compareTo(ParameterAnnotation o) { + return ((Integer)method.getReference().getIndex()).compareTo(o.method.getReference().getIndex()); + } } } diff --git a/src/main/java/org/JesusFreke/dexlib/AnnotationItem.java b/src/main/java/org/JesusFreke/dexlib/AnnotationItem.java index dc1a077d..6b2a72ff 100644 --- a/src/main/java/org/JesusFreke/dexlib/AnnotationItem.java +++ b/src/main/java/org/JesusFreke/dexlib/AnnotationItem.java @@ -46,6 +46,16 @@ public class AnnotationItem extends OffsettedItem { }; } + public AnnotationItem(DexFile dexFile, AnnotationVisibility visibility, + AnnotationEncodedValueSubField annotation) { + super(-1); + + fields = new Field[] { + this.visibility = new ByteField(visibility.value), + this.annotation = annotation + }; + } + protected int getAlignment() { return 1; } diff --git a/src/main/java/org/JesusFreke/dexlib/AnnotationSetItem.java b/src/main/java/org/JesusFreke/dexlib/AnnotationSetItem.java index 5e528dbb..c5e8b2ed 100644 --- a/src/main/java/org/JesusFreke/dexlib/AnnotationSetItem.java +++ b/src/main/java/org/JesusFreke/dexlib/AnnotationSetItem.java @@ -31,6 +31,7 @@ package org.JesusFreke.dexlib; import org.JesusFreke.dexlib.ItemType; import java.util.ArrayList; +import java.util.List; public class AnnotationSetItem extends OffsettedItem { private final Field[] fields; @@ -48,12 +49,22 @@ public class AnnotationSetItem extends OffsettedItem { annotationCount = new ListSizeField(annotationReferences, new IntegerField()), annotations = new FieldListField>(annotationReferences) { protected OffsettedItemReference make() { - return new OffsettedItemReference(dexFile.AnnotationsSection, new IntegerField()); + return new OffsettedItemReference(dexFile.AnnotationsSection, + new IntegerField()); } } }; } + public AnnotationSetItem(final DexFile dexFile, List annotations) { + this(dexFile, -1); + + for (AnnotationItem annotationItem: annotations) { + this.annotationReferences.add(new OffsettedItemReference(dexFile, + annotationItem, new IntegerField())); + } + } + protected int getAlignment() { return 4; } diff --git a/src/main/java/org/JesusFreke/dexlib/AnnotationSetRefList.java b/src/main/java/org/JesusFreke/dexlib/AnnotationSetRefList.java index 0bf905e6..90188dfd 100644 --- a/src/main/java/org/JesusFreke/dexlib/AnnotationSetRefList.java +++ b/src/main/java/org/JesusFreke/dexlib/AnnotationSetRefList.java @@ -31,6 +31,7 @@ package org.JesusFreke.dexlib; import org.JesusFreke.dexlib.ItemType; import java.util.ArrayList; +import java.util.List; public class AnnotationSetRefList extends OffsettedItem { private final Field[] fields; @@ -54,6 +55,14 @@ public class AnnotationSetRefList extends OffsettedItem { }; } + public AnnotationSetRefList(final DexFile dexFile, List annotationSets) { + this(dexFile, -1); + + for (AnnotationSetItem annotation: annotationSets) { + this.annotationSetReferences.add(new OffsettedItemReference(dexFile, annotation, new IntegerField())); + } + } + protected int getAlignment() { return 4; } diff --git a/src/main/java/org/JesusFreke/dexlib/ClassDefItem.java b/src/main/java/org/JesusFreke/dexlib/ClassDefItem.java index 3ef22945..f24b0f65 100644 --- a/src/main/java/org/JesusFreke/dexlib/ClassDefItem.java +++ b/src/main/java/org/JesusFreke/dexlib/ClassDefItem.java @@ -69,7 +69,13 @@ public class ClassDefItem extends IndexedItem { }; } - public ClassDefItem(DexFile dexFile, TypeIdItem classType, int accessFlags, TypeIdItem superType, TypeListItem implementsList, StringIdItem source, ClassDataItem classDataItem) { + public ClassDefItem(DexFile dexFile, + TypeIdItem classType, + int accessFlags, + TypeIdItem superType, + TypeListItem implementsList, + StringIdItem source, + ClassDataItem classDataItem) { super(-1); this.dexFile = dexFile; @@ -132,10 +138,9 @@ public class ClassDefItem extends IndexedItem { return 0; } - public void addMethod(ClassDataItem.EncodedMethod encodedMethod) { - } - public void addField(ClassDataItem.EncodedField encodedField, EncodedValue initialValue) { + //fields are added in ClassDefItem instead of ClassDataItem because we need to grab + //the static initializers for StaticFieldInitialValues if (!encodedField.isStatic() && initialValue != null) { throw new RuntimeException("Initial values are only allowed for static fields."); } @@ -163,6 +168,10 @@ public class ClassDefItem extends IndexedItem { } } + public void setAnnotations(AnnotationDirectoryItem annotations) { + this.classAnnotations.setReference(annotations); + } + public static int placeClassDefItems(IndexedSection section, int offset) { ClassDefPlacer cdp = new ClassDefPlacer(section); return cdp.placeSection(offset); diff --git a/src/main/java/org/JesusFreke/dexlib/EncodedValue/AnnotationElement.java b/src/main/java/org/JesusFreke/dexlib/EncodedValue/AnnotationElement.java index 2bbbbc9d..ecbd1467 100644 --- a/src/main/java/org/JesusFreke/dexlib/EncodedValue/AnnotationElement.java +++ b/src/main/java/org/JesusFreke/dexlib/EncodedValue/AnnotationElement.java @@ -42,6 +42,13 @@ public class AnnotationElement extends CompositeField { encodedValue = new EncodedValue(dexFile) }; } + + public AnnotationElement(final DexFile dexFile, StringIdItem elementName, EncodedValue encodedValue) { + fields = new Field[] { + this.elementName = new IndexedItemReference(dexFile, elementName, new Leb128Field()), + this.encodedValue = encodedValue + }; + } protected Field[] getFields() { return fields; diff --git a/src/main/java/org/JesusFreke/dexlib/EncodedValue/AnnotationEncodedValueSubField.java b/src/main/java/org/JesusFreke/dexlib/EncodedValue/AnnotationEncodedValueSubField.java index 91477717..93da288d 100644 --- a/src/main/java/org/JesusFreke/dexlib/EncodedValue/AnnotationEncodedValueSubField.java +++ b/src/main/java/org/JesusFreke/dexlib/EncodedValue/AnnotationEncodedValueSubField.java @@ -31,6 +31,7 @@ package org.JesusFreke.dexlib.EncodedValue; import org.JesusFreke.dexlib.*; import java.util.ArrayList; +import java.util.List; public class AnnotationEncodedValueSubField extends CompositeField implements EncodedValueSubField { @@ -54,6 +55,13 @@ public class AnnotationEncodedValueSubField extends CompositeField annotationElements) { + this(dexFile); + this.annotationType.setReference(annotationType); + this.annotationElementList.addAll(annotationElements); + } + protected Field[] getFields() { return fields; } diff --git a/src/main/java/org/JesusFreke/dexlib/util/DebugInfoBuilder.java b/src/main/java/org/JesusFreke/dexlib/util/DebugInfoBuilder.java index b1a0d127..d5bf2650 100644 --- a/src/main/java/org/JesusFreke/dexlib/util/DebugInfoBuilder.java +++ b/src/main/java/org/JesusFreke/dexlib/util/DebugInfoBuilder.java @@ -72,7 +72,9 @@ public class DebugInfoBuilder } public void addParameterName(String parameterName) { - hasData = true; + if (parameterName != null) { + hasData = true; + } parameterNames.add(parameterName); }