- Added support for parameter name debug info

- Changed the grammar so that the .registers directive can appear anywhere in the .method, instead of requiring it to be the first thing

git-svn-id: https://smali.googlecode.com/svn/trunk@45 55b6fa8a-2a1e-11de-a435-ffa8d773f76a
This commit is contained in:
JesusFreke@JesusFreke.com 2009-05-22 00:42:12 +00:00
parent aa7e507bac
commit 66a23e6ecc
8 changed files with 151 additions and 37 deletions

View File

@ -227,11 +227,6 @@ METHOD_PHRASE
END_METHOD_PHRASE END_METHOD_PHRASE
: END_METHOD_DIRECTIVE_EMIT; : END_METHOD_DIRECTIVE_EMIT;
REGISTERS_PHRASE
: REGISTERS_DIRECTIVE_EMIT
WS
INTEGRAL_LITERAL_EMITCHILD;
INSTRUCTION_FORMAT10t_PHRASE INSTRUCTION_FORMAT10t_PHRASE
: INSTRUCTION_FORMAT10t_EMIT : INSTRUCTION_FORMAT10t_EMIT
WS WS
@ -453,6 +448,11 @@ SPARSE_SWITCH_PHRASE
WSC WSC
END_SPARSE_SWITCH_DIRECTIVE_EMIT; END_SPARSE_SWITCH_DIRECTIVE_EMIT;
REGISTERS_PHRASE
: REGISTERS_DIRECTIVE_EMIT
WS
INTEGRAL_LITERAL_EMITCHILD;
CATCH_PHRASE CATCH_PHRASE
: CATCH_DIRECTIVE_EMIT : CATCH_DIRECTIVE_EMIT
WS WS
@ -468,6 +468,10 @@ LINE_PHRASE
: LINE_DIRECTIVE_EMIT : LINE_DIRECTIVE_EMIT
WS WS
INTEGRAL_LITERAL_EMITCHILD; INTEGRAL_LITERAL_EMITCHILD;
PARAMETER_PHRASE
: PARAMETER_DIRECTIVE_EMIT
(WS STRING_LITERAL_EMIT?)?;
//TODO: add support for both relative and absolute offsets? //TODO: add support for both relative and absolute offsets?
@ -516,11 +520,6 @@ fragment END_METHOD_DIRECTIVE_EMIT
fragment END_METHOD_DIRECTIVE fragment END_METHOD_DIRECTIVE
: '.end method'; : '.end method';
fragment REGISTERS_DIRECTIVE_EMIT
: REGISTERS_DIRECTIVE {emit($REGISTERS_DIRECTIVE, REGISTERS_DIRECTIVE);};
fragment REGISTERS_DIRECTIVE
: '.registers';
fragment ARRAY_DATA_DIRECTIVE_EMIT fragment ARRAY_DATA_DIRECTIVE_EMIT
: ARRAY_DATA_DIRECTIVE {emit($ARRAY_DATA_DIRECTIVE, ARRAY_DATA_DIRECTIVE);}; : ARRAY_DATA_DIRECTIVE {emit($ARRAY_DATA_DIRECTIVE, ARRAY_DATA_DIRECTIVE);};
fragment ARRAY_DATA_DIRECTIVE fragment ARRAY_DATA_DIRECTIVE
@ -551,6 +550,11 @@ fragment END_SPARSE_SWITCH_DIRECTIVE_EMIT
fragment END_SPARSE_SWITCH_DIRECTIVE fragment END_SPARSE_SWITCH_DIRECTIVE
: '.end sparse-switch'; : '.end sparse-switch';
fragment REGISTERS_DIRECTIVE_EMIT
: REGISTERS_DIRECTIVE {emit($REGISTERS_DIRECTIVE, REGISTERS_DIRECTIVE);};
fragment REGISTERS_DIRECTIVE
: '.registers';
fragment CATCH_DIRECTIVE_EMIT fragment CATCH_DIRECTIVE_EMIT
: CATCH_DIRECTIVE {emit($CATCH_DIRECTIVE, CATCH_DIRECTIVE);}; : CATCH_DIRECTIVE {emit($CATCH_DIRECTIVE, CATCH_DIRECTIVE);};
fragment CATCH_DIRECTIVE fragment CATCH_DIRECTIVE
@ -561,6 +565,11 @@ fragment LINE_DIRECTIVE_EMIT
fragment LINE_DIRECTIVE fragment LINE_DIRECTIVE
: '.line'; : '.line';
fragment PARAMETER_DIRECTIVE_EMIT
: PARAMETER_DIRECTIVE {emit($PARAMETER_DIRECTIVE, PARAMETER_DIRECTIVE);};
fragment PARAMETER_DIRECTIVE
: '.parameter';
fragment REGISTER_EMIT fragment REGISTER_EMIT
: REGISTER {emit($REGISTER, REGISTER);}; : REGISTER {emit($REGISTER, REGISTER);};
fragment REGISTER fragment REGISTER

View File

@ -67,6 +67,9 @@ tokens {
I_CATCH_ADDRESS; I_CATCH_ADDRESS;
I_LINE; I_LINE;
I_LINES; I_LINES;
I_PARAMETER;
I_PARAMETERS;
I_PARAMETER_NOT_SPECIFIED;
I_STATEMENTS; I_STATEMENTS;
I_STATEMENT_FORMAT10t; I_STATEMENT_FORMAT10t;
I_STATEMENT_FORMAT10x; I_STATEMENT_FORMAT10x;
@ -187,48 +190,68 @@ access_list
field : FIELD_DIRECTIVE access_list MEMBER_NAME field_type_descriptor literal? 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)?); -> ^(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 method
registers_directive scope {int currentAddress;}
statements : {$method::currentAddress = 0;}
METHOD_DIRECTIVE access_list MEMBER_NAME method_prototype
statements_and_directives
END_METHOD_DIRECTIVE 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 method_prototype
: OPEN_PAREN field_type_descriptor* CLOSE_PAREN type_descriptor : 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*); -> ^(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 fully_qualified_method
: CLASS_NAME MEMBER_NAME method_prototype; : CLASS_NAME MEMBER_NAME method_prototype;
fully_qualified_field fully_qualified_field
: CLASS_NAME MEMBER_NAME field_type_descriptor; : CLASS_NAME MEMBER_NAME field_type_descriptor;
statements statements_and_directives
scope {int currentAddress;} : {
: {$statements::currentAddress = 0;} $method::currentAddress = 0;
( instruction {$statements::currentAddress += $instruction.size/2;} boolean hasRegistersDirective = false;
}
( instruction {$method::currentAddress += $instruction.size/2;}
| {!hasRegistersDirective}?=> registers_directive {hasRegistersDirective = true;}
| catch_directive | catch_directive
| line_directive | line_directive
| parameter_directive
| label)* | 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
: CATCH_DIRECTIVE field_type_descriptor from=offset_or_label to=offset_or_label using=offset_or_label : 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
: 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
: 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] instruction returns [int size]
@init {boolean needsNop = false;} @init {boolean needsNop = false;}
@ -317,7 +340,7 @@ instruction returns [int size]
| |
ARRAY_DATA_DIRECTIVE ARRAY_DATA_DIRECTIVE
{ {
if (($statements::currentAddress \% 2) != 0) { if (($method::currentAddress \% 2) != 0) {
needsNop = true; needsNop = true;
$size = 2; $size = 2;
} else { } else {
@ -337,7 +360,7 @@ instruction returns [int size]
PACKED_SWITCH_DIRECTIVE PACKED_SWITCH_DIRECTIVE
{ {
int targetCount = 0; int targetCount = 0;
if (($statements::currentAddress \% 2) != 0) { if (($method::currentAddress \% 2) != 0) {
needsNop = true; needsNop = true;
$size = 2; $size = 2;
} else { } else {
@ -371,7 +394,7 @@ instruction returns [int size]
SPARSE_SWITCH_DIRECTIVE SPARSE_SWITCH_DIRECTIVE
{ {
int targetCount = 0; int targetCount = 0;
if (($statements::currentAddress \% 2) != 0) { if (($method::currentAddress \% 2) != 0) {
needsNop = true; needsNop = true;
$size = 2; $size = 2;
} else { } else {

View File

@ -294,17 +294,44 @@ method returns[ClassDataItem.EncodedMethod encodedMethod]
$method::currentAddress = 0; $method::currentAddress = 0;
$method::debugInfo = new DebugInfoBuilder(); $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; MethodIdItem methodIdItem = $method_name_and_prototype.methodIdItem;
int registers = $registers_directive.registers; int registers = $registers_directive.registers;
int access = $access_list.value; int access = $access_list.value;
boolean isStatic = (access & AccessFlags.STATIC) != 0; boolean isStatic = (access & AccessFlags.STATIC) != 0;
ArrayList<Instruction> instructions = $statements.instructions; ArrayList<Instruction> 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<CodeItem.TryItem>, List<CodeItem.EncodedCatchHandler>> temp = $method::tryList.encodeTries(dexFile); Pair<List<CodeItem.TryItem>, List<CodeItem.EncodedCatchHandler>> temp = $method::tryList.encodeTries(dexFile);
List<CodeItem.TryItem> tries = temp.first; List<CodeItem.TryItem> tries = temp.first;
List<CodeItem.EncodedCatchHandler> handlers = temp.second; List<CodeItem.EncodedCatchHandler> 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); DebugInfoItem debugInfoItem = $method::debugInfo.encodeDebugInfo(dexFile);
@ -351,10 +378,6 @@ field_type_list returns[ArrayList<TypeIdItem> types]
} }
)*; )*;
registers_directive returns[int registers]
: ^(I_REGISTERS short_integral_literal) {$registers = $short_integral_literal.value;};
fully_qualified_method returns[MethodIdItem methodIdItem] fully_qualified_method returns[MethodIdItem methodIdItem]
: CLASS_NAME MEMBER_NAME method_prototype : CLASS_NAME MEMBER_NAME method_prototype
@ -373,6 +396,9 @@ fully_qualified_field returns[FieldIdItem fieldIdItem]
TypeIdItem fieldType = $field_type_descriptor.type; TypeIdItem fieldType = $field_type_descriptor.type;
$fieldIdItem = new FieldIdItem(dexFile, classType, fieldName, fieldType); $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*); catches : ^(I_CATCHES catch_directive*);
@ -395,6 +421,15 @@ line
{ {
$method::debugInfo.addLine($integer_literal.value, $integral_literal.value); $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 labels
: ^(I_LABELS label_def*); : ^(I_LABELS label_def*);

View File

@ -90,6 +90,14 @@ public class MethodIdItem extends IndexedItem<MethodIdItem> {
return prototype.getReference().getParameterWordCount() + (isStatic?0:1); 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) { public int compareTo(MethodIdItem o) {
int result = classType.compareTo(o.classType); int result = classType.compareTo(o.classType);
if (result != 0) { if (result != 0) {

View File

@ -84,6 +84,16 @@ public class ProtoIdItem extends IndexedItem<ProtoIdItem> {
} }
} }
public int getParameterCount() {
TypeListItem typeList = parameters.getReference();
if (typeList == null) {
return 0;
} else {
return typeList.getCount();
}
}
protected Field[] getFields() { protected Field[] getFields() {
return fields; return fields;
} }

View File

@ -79,6 +79,10 @@ public class TypeListItem extends OffsettedItem<TypeListItem> implements Compara
return wordCount; return wordCount;
} }
public int getCount() {
return typeList.size();
}
public int getAlignment() { public int getAlignment() {
return 4; return 4;
} }

View File

@ -78,6 +78,16 @@ public class DebugInfoBuilder
events.add(new LineEvent(address, line)); 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) { public DebugInfoItem encodeDebugInfo(DexFile dexFile) {
if (!hasData) { if (!hasData) {
return null; return null;
@ -98,7 +108,11 @@ public class DebugInfoBuilder
debugInstructions.add(new EndSequence()); debugInstructions.add(new EndSequence());
for (String parameterName: parameterNames) { 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); return new DebugInfoItem(dexFile, lineStart, parameterNameReferences, debugInstructions);

View File

@ -20,7 +20,18 @@
.method public onAccountsUpdated([Ljava/lang/String;)V .method public onAccountsUpdated([Ljava/lang/String;)V
.parameter "currentAccounts"
return-void
.registers 2 .registers 2
.end method
.method public static parameterNameTest(IIII)V
.registers 4
.parameter "test1"
.parameter "test2"
.parameter
.parameter "test4"
return-void return-void
.end method .end method