diff --git a/src/main/antlr3/org/JesusFreke/smali/smaliLexer.g b/src/main/antlr3/org/JesusFreke/smali/smaliLexer.g index cd774499..e6e953f8 100644 --- a/src/main/antlr3/org/JesusFreke/smali/smaliLexer.g +++ b/src/main/antlr3/org/JesusFreke/smali/smaliLexer.g @@ -227,11 +227,6 @@ METHOD_PHRASE END_METHOD_PHRASE : END_METHOD_DIRECTIVE_EMIT; -REGISTERS_PHRASE - : REGISTERS_DIRECTIVE_EMIT - WS - INTEGRAL_LITERAL_EMITCHILD; - INSTRUCTION_FORMAT10t_PHRASE : INSTRUCTION_FORMAT10t_EMIT WS @@ -453,6 +448,11 @@ SPARSE_SWITCH_PHRASE WSC END_SPARSE_SWITCH_DIRECTIVE_EMIT; +REGISTERS_PHRASE + : REGISTERS_DIRECTIVE_EMIT + WS + INTEGRAL_LITERAL_EMITCHILD; + CATCH_PHRASE : CATCH_DIRECTIVE_EMIT WS @@ -468,6 +468,10 @@ LINE_PHRASE : LINE_DIRECTIVE_EMIT WS INTEGRAL_LITERAL_EMITCHILD; + +PARAMETER_PHRASE + : PARAMETER_DIRECTIVE_EMIT + (WS STRING_LITERAL_EMIT?)?; //TODO: add support for both relative and absolute offsets? @@ -516,11 +520,6 @@ fragment END_METHOD_DIRECTIVE_EMIT fragment END_METHOD_DIRECTIVE : '.end method'; -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 @@ -551,6 +550,11 @@ fragment END_SPARSE_SWITCH_DIRECTIVE_EMIT fragment END_SPARSE_SWITCH_DIRECTIVE : '.end sparse-switch'; +fragment REGISTERS_DIRECTIVE_EMIT + : REGISTERS_DIRECTIVE {emit($REGISTERS_DIRECTIVE, REGISTERS_DIRECTIVE);}; +fragment REGISTERS_DIRECTIVE + : '.registers'; + fragment CATCH_DIRECTIVE_EMIT : CATCH_DIRECTIVE {emit($CATCH_DIRECTIVE, CATCH_DIRECTIVE);}; fragment CATCH_DIRECTIVE @@ -561,6 +565,11 @@ fragment LINE_DIRECTIVE_EMIT fragment LINE_DIRECTIVE : '.line'; +fragment PARAMETER_DIRECTIVE_EMIT + : PARAMETER_DIRECTIVE {emit($PARAMETER_DIRECTIVE, PARAMETER_DIRECTIVE);}; +fragment PARAMETER_DIRECTIVE + : '.parameter'; + fragment REGISTER_EMIT : REGISTER {emit($REGISTER, REGISTER);}; fragment REGISTER diff --git a/src/main/antlr3/org/JesusFreke/smali/smaliParser.g b/src/main/antlr3/org/JesusFreke/smali/smaliParser.g index bbeea421..5d57e291 100644 --- a/src/main/antlr3/org/JesusFreke/smali/smaliParser.g +++ b/src/main/antlr3/org/JesusFreke/smali/smaliParser.g @@ -67,6 +67,9 @@ tokens { I_CATCH_ADDRESS; I_LINE; I_LINES; + I_PARAMETER; + I_PARAMETERS; + I_PARAMETER_NOT_SPECIFIED; I_STATEMENTS; I_STATEMENT_FORMAT10t; I_STATEMENT_FORMAT10x; @@ -187,48 +190,68 @@ access_list 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)?); -method : METHOD_DIRECTIVE access_list MEMBER_NAME method_prototype - registers_directive - statements +method + scope {int currentAddress;} + : {$method::currentAddress = 0;} + METHOD_DIRECTIVE access_list MEMBER_NAME method_prototype + statements_and_directives END_METHOD_DIRECTIVE - -> ^(I_METHOD[$start, "I_METHOD"] MEMBER_NAME method_prototype access_list registers_directive statements); + -> ^(I_METHOD[$start, "I_METHOD"] MEMBER_NAME method_prototype access_list statements_and_directives); method_prototype : OPEN_PAREN field_type_descriptor* CLOSE_PAREN type_descriptor -> ^(I_METHOD_PROTOTYPE[$start, "I_METHOD_PROTOTYPE"] ^(I_METHOD_RETURN_TYPE type_descriptor) field_type_descriptor*); - -registers_directive - : REGISTERS_DIRECTIVE integral_literal - -> ^(I_REGISTERS[$start, "I_REGISTERS"] integral_literal); - - fully_qualified_method : CLASS_NAME MEMBER_NAME method_prototype; fully_qualified_field : CLASS_NAME MEMBER_NAME field_type_descriptor; -statements - scope {int currentAddress;} - : {$statements::currentAddress = 0;} - ( instruction {$statements::currentAddress += $instruction.size/2;} +statements_and_directives + : { + $method::currentAddress = 0; + boolean hasRegistersDirective = false; + } + ( instruction {$method::currentAddress += $instruction.size/2;} + | {!hasRegistersDirective}?=> registers_directive {hasRegistersDirective = true;} | catch_directive | line_directive + | parameter_directive | label)* - -> ^(I_LABELS label*) ^(I_STATEMENTS instruction*) ^(I_CATCHES catch_directive*) ^(I_LINES line_directive*); + { + if (!hasRegistersDirective) { + //TODO: throw correct exception type here + throw new RuntimeException("This method has no register directive"); + } + } + -> registers_directive + ^(I_LABELS label*) + ^(I_STATEMENTS instruction*) + ^(I_CATCHES catch_directive*) + ^(I_LINES line_directive*) + ^(I_PARAMETERS parameter_directive*); + +registers_directive + : REGISTERS_DIRECTIVE integral_literal + -> ^(I_REGISTERS[$start, "I_REGISTERS"] integral_literal); catch_directive : CATCH_DIRECTIVE field_type_descriptor from=offset_or_label to=offset_or_label using=offset_or_label - -> ^(I_CATCH[$start, "I_CATCH"] I_CATCH_ADDRESS[$start, Integer.toString($statements::currentAddress)] field_type_descriptor $from $to $using) + -> ^(I_CATCH[$start, "I_CATCH"] I_CATCH_ADDRESS[$start, Integer.toString($method::currentAddress)] field_type_descriptor $from $to $using) ; line_directive - : LINE_DIRECTIVE integral_literal -> ^(I_LINE integral_literal {new CommonTree(new CommonToken(INTEGER_LITERAL,Integer.toString($statements::currentAddress)))}); + : LINE_DIRECTIVE integral_literal -> ^(I_LINE integral_literal {new CommonTree(new CommonToken(INTEGER_LITERAL,Integer.toString($method::currentAddress)))}); + +parameter_directive + : PARAMETER_DIRECTIVE ( STRING_LITERAL -> ^(I_PARAMETER STRING_LITERAL?) + | -> ^(I_PARAMETER I_PARAMETER_NOT_SPECIFIED) + ); label - : LABEL -> ^(I_LABEL LABEL {new CommonTree(new CommonToken(INTEGER_LITERAL,Integer.toString($statements::currentAddress)))}); + : LABEL -> ^(I_LABEL LABEL {new CommonTree(new CommonToken(INTEGER_LITERAL,Integer.toString($method::currentAddress)))}); instruction returns [int size] @init {boolean needsNop = false;} @@ -317,7 +340,7 @@ instruction returns [int size] | ARRAY_DATA_DIRECTIVE { - if (($statements::currentAddress \% 2) != 0) { + if (($method::currentAddress \% 2) != 0) { needsNop = true; $size = 2; } else { @@ -337,7 +360,7 @@ instruction returns [int size] PACKED_SWITCH_DIRECTIVE { int targetCount = 0; - if (($statements::currentAddress \% 2) != 0) { + if (($method::currentAddress \% 2) != 0) { needsNop = true; $size = 2; } else { @@ -371,7 +394,7 @@ instruction returns [int size] SPARSE_SWITCH_DIRECTIVE { int targetCount = 0; - if (($statements::currentAddress \% 2) != 0) { + if (($method::currentAddress \% 2) != 0) { needsNop = true; $size = 2; } else { diff --git a/src/main/antlr3/org/JesusFreke/smali/smaliTreeWalker.g b/src/main/antlr3/org/JesusFreke/smali/smaliTreeWalker.g index d5c3bb15..2937046d 100644 --- a/src/main/antlr3/org/JesusFreke/smali/smaliTreeWalker.g +++ b/src/main/antlr3/org/JesusFreke/smali/smaliTreeWalker.g @@ -294,17 +294,44 @@ method returns[ClassDataItem.EncodedMethod encodedMethod] $method::currentAddress = 0; $method::debugInfo = new DebugInfoBuilder(); } - ^(I_METHOD method_name_and_prototype access_list registers_directive labels statements catches lines) + ^( I_METHOD + method_name_and_prototype + access_list + registers_directive + labels + statements + catches + lines + parameters) { MethodIdItem methodIdItem = $method_name_and_prototype.methodIdItem; int registers = $registers_directive.registers; int access = $access_list.value; boolean isStatic = (access & AccessFlags.STATIC) != 0; ArrayList instructions = $statements.instructions; + + + int minRegisters = methodIdItem.getParameterWordCount((access & AccessFlags.STATIC) != 0); + + if (registers < minRegisters) { + //TODO: throw the correct exception type + throw new RuntimeException( "This method requires at least " + + Integer.toString(minRegisters) + + " registers, for the method parameters"); + } Pair, List> temp = $method::tryList.encodeTries(dexFile); List tries = temp.first; List handlers = temp.second; + + + int methodParameterCount = methodIdItem.getParameterCount(); + if ($method::debugInfo.getParameterNameCount() > methodParameterCount) { + //TODO: throw the correct exception type + throw new RuntimeException( "Too many parameter names specified. This method only has " + + Integer.toString(methodParameterCount) + + " parameters."); + } DebugInfoItem debugInfoItem = $method::debugInfo.encodeDebugInfo(dexFile); @@ -351,10 +378,6 @@ field_type_list returns[ArrayList types] } )*; -registers_directive returns[int registers] - : ^(I_REGISTERS short_integral_literal) {$registers = $short_integral_literal.value;}; - - fully_qualified_method returns[MethodIdItem methodIdItem] : CLASS_NAME MEMBER_NAME method_prototype @@ -373,6 +396,9 @@ fully_qualified_field returns[FieldIdItem fieldIdItem] TypeIdItem fieldType = $field_type_descriptor.type; $fieldIdItem = new FieldIdItem(dexFile, classType, fieldName, fieldType); }; + +registers_directive returns[int registers] + : ^(I_REGISTERS short_integral_literal) {$registers = $short_integral_literal.value;}; catches : ^(I_CATCHES catch_directive*); @@ -395,6 +421,15 @@ line { $method::debugInfo.addLine($integer_literal.value, $integral_literal.value); }; + +parameters + : ^(I_PARAMETERS parameter*); + +parameter + : ^(I_PARAMETER ( + string_literal {$method::debugInfo.addParameterName($string_literal.value);} + | I_PARAMETER_NOT_SPECIFIED {$method::debugInfo.addParameterName(null);} + )); labels : ^(I_LABELS label_def*); diff --git a/src/main/java/org/JesusFreke/dexlib/MethodIdItem.java b/src/main/java/org/JesusFreke/dexlib/MethodIdItem.java index 38e28522..d86c81f1 100644 --- a/src/main/java/org/JesusFreke/dexlib/MethodIdItem.java +++ b/src/main/java/org/JesusFreke/dexlib/MethodIdItem.java @@ -90,6 +90,14 @@ public class MethodIdItem extends IndexedItem { return prototype.getReference().getParameterWordCount() + (isStatic?0:1); } + /** + * Return the number of parameters, not including the "this" parameter, if any + * @return The number of parameters, not including the "this" parameter, if any + */ + public int getParameterCount() { + return prototype.getReference().getParameterCount(); + } + public int compareTo(MethodIdItem o) { int result = classType.compareTo(o.classType); if (result != 0) { diff --git a/src/main/java/org/JesusFreke/dexlib/ProtoIdItem.java b/src/main/java/org/JesusFreke/dexlib/ProtoIdItem.java index 737e90ca..96aac46d 100644 --- a/src/main/java/org/JesusFreke/dexlib/ProtoIdItem.java +++ b/src/main/java/org/JesusFreke/dexlib/ProtoIdItem.java @@ -84,6 +84,16 @@ public class ProtoIdItem extends IndexedItem { } } + + public int getParameterCount() { + TypeListItem typeList = parameters.getReference(); + if (typeList == null) { + return 0; + } else { + return typeList.getCount(); + } + } + protected Field[] getFields() { return fields; } diff --git a/src/main/java/org/JesusFreke/dexlib/TypeListItem.java b/src/main/java/org/JesusFreke/dexlib/TypeListItem.java index a1d4b5ee..1a6c0ff6 100644 --- a/src/main/java/org/JesusFreke/dexlib/TypeListItem.java +++ b/src/main/java/org/JesusFreke/dexlib/TypeListItem.java @@ -79,6 +79,10 @@ public class TypeListItem extends OffsettedItem implements Compara return wordCount; } + public int getCount() { + return typeList.size(); + } + public int getAlignment() { return 4; } diff --git a/src/main/java/org/JesusFreke/dexlib/util/DebugInfoBuilder.java b/src/main/java/org/JesusFreke/dexlib/util/DebugInfoBuilder.java index d18f5d57..cf318281 100644 --- a/src/main/java/org/JesusFreke/dexlib/util/DebugInfoBuilder.java +++ b/src/main/java/org/JesusFreke/dexlib/util/DebugInfoBuilder.java @@ -78,6 +78,16 @@ public class DebugInfoBuilder events.add(new LineEvent(address, line)); } + public void addParameterName(String parameterName) { + hasData = true; + + parameterNames.add(parameterName); + } + + public int getParameterNameCount() { + return parameterNames.size(); + } + public DebugInfoItem encodeDebugInfo(DexFile dexFile) { if (!hasData) { return null; @@ -98,7 +108,11 @@ public class DebugInfoBuilder debugInstructions.add(new EndSequence()); for (String parameterName: parameterNames) { - parameterNameReferences.add(new StringIdItem(dexFile, parameterName)); + if (parameterName == null) { + parameterNameReferences.add(null); + } else { + parameterNameReferences.add(new StringIdItem(dexFile, parameterName)); + } } return new DebugInfoItem(dexFile, lineStart, parameterNameReferences, debugInstructions); diff --git a/src/test/resources/examples/HelloWorld2.smali b/src/test/resources/examples/HelloWorld2.smali index fe1ae031..244b4658 100644 --- a/src/test/resources/examples/HelloWorld2.smali +++ b/src/test/resources/examples/HelloWorld2.smali @@ -20,7 +20,18 @@ .method public onAccountsUpdated([Ljava/lang/String;)V + .parameter "currentAccounts" + + return-void .registers 2 +.end method + +.method public static parameterNameTest(IIII)V + .registers 4 + .parameter "test1" + .parameter "test2" + .parameter + .parameter "test4" return-void .end method