- 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_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

View File

@ -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 {

View File

@ -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<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);
List<CodeItem.TryItem> tries = temp.first;
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);
@ -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]
: 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*);

View File

@ -90,6 +90,14 @@ public class MethodIdItem extends IndexedItem<MethodIdItem> {
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) {

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() {
return fields;
}

View File

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

View File

@ -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);

View File

@ -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