@ -1,220 +1,168 @@
/ * *
* Methods in this file walk the AST and call the instrumenter
* functions where appropriate , which determine where to inject events .
* ( Listed in alphabetical order )
* /
const parse = { } ;
const instrumenter = require ( './instrumenter' ) ;
// All the functions in this file do is walk the AST and call the instrumenter
// functions where appropriate, which determine where to inject events.
// Assign a dummy function to everything we might see in the AST
[ 'AssemblyAssignment' ,
'AssemblyItem' ,
'AssemblyLocalBinding' ,
'AssignmentExpression' ,
'BinaryExpression' ,
'BlockStatement' ,
'BreakStatement' ,
'CallExpression' ,
'ConditionalExpression' ,
'ContinueStatement' ,
'ContractStatement' ,
'DebuggerStatement' ,
'DeclarativeExpression' ,
'DeclarativeExpressionList' ,
'DenominationLiteral' ,
'DoWhileStatement' ,
'EmptyStatement' ,
'EnumDeclaration' ,
'EventDeclaration' ,
'ExpressionStatement' ,
'ForInStatement' ,
'ForStatement' ,
'FunctionalAssemblyExpression' ,
'FunctionDeclaration' ,
'FunctionName' ,
'Identifier' ,
'IfStatement' ,
'ImportStatement' ,
'InformalParameter' ,
'InlineAssemblyBlock' ,
'InlineAssemblyStatement' ,
'IsStatement' ,
'LibraryStatement' ,
'Literal' ,
'MappingExpression' ,
'MemberExpression' ,
'ModifierArgument' ,
'ModifierDeclaration' ,
'ModifierName' ,
'Modifiers' ,
'NewExpression' ,
'PlaceholderStatement' ,
'PragmaStatement' ,
'Program' ,
'ReturnStatement' ,
'SequenceExpression' ,
'StateVariableDeclaration' ,
'StructDeclaration' ,
'ThisExpression' ,
'ThrowStatement' ,
'Type' ,
'UnaryExpression' ,
'UpdateExpression' ,
'UsingStatement' ,
'VariableDeclaration' ,
'VariableDeclarationTuple' ,
'VariableDeclarator' ,
'WhileStatement' ] . forEach ( key => {
parse [ key ] = function dummyFunction ( ) { } ;
} ) ;
// Override the dummy functions where we know we can do better.
parse . AssignmentExpression = function parseAssignmentExpression ( contract , expression ) {
instrumenter . instrumentStatement ( contract , expression ) ;
instrumenter . instrumentAssignmentExpression ( contract , expression ) ;
} ;
parse . ConditionalExpression = function parseConditionalExpression ( contract , expression ) {
instrumenter . instrumentStatement ( contract , expression ) ;
instrumenter . instrumentConditionalExpression ( contract , expression ) ;
} ;
parse . Modifiers = function parseModifier ( contract , modifiers ) {
if ( modifiers ) {
modifiers . forEach ( modifier => {
parse [ modifier . type ] ( contract , modifier ) ;
} ) ;
parse . BlockStatement = function parseBlockStatement ( contract , expression ) {
for ( let x = 0 ; x < expression . body . length ; x ++ ) {
instrumenter . instrumentLine ( contract , expression . body [ x ] ) ;
parse [ expression . body [ x ] . type ] &&
parse [ expression . body [ x ] . type ] ( contract , expression . body [ x ] ) ;
}
} ;
parse . ReturnStatement = function parseReturnStatement ( contract , expression ) {
instrumenter . instrumentStatement ( contract , expression ) ;
} ;
parse . NewExpression = function parseNewExpression ( contract , expression ) {
parse [ expression . callee . type ] ( contract , expression . callee ) ;
} ;
parse . MemberExpression = function parseMemberExpression ( contract , expression ) {
parse [ expression . object . type ] ( contract , expression . object ) ;
} ;
parse . CallExpression = function parseCallExpression ( contract , expression ) {
// It looks like in any given chain of call expressions, only the head callee is an Identifier
// node ... and we only want to instrument the statement once.
// In any given chain of call expressions, only the head callee is an Identifier node
if ( expression . callee . type === 'Identifier' ) {
instrumenter . instrumentStatement ( contract , expression ) ;
parse [ expression . callee . type ] &&
parse [ expression . callee . type ] ( contract , expression . callee ) ;
} else {
parse [ expression . callee . type ] &&
parse [ expression . callee . type ] ( contract , expression . callee ) ;
}
} ;
parse . UnaryExpression = function parseUnaryExpression ( contract , expression ) {
parse [ expression . argument . type ] ( contract , expression . argument ) ;
parse . ConditionalExpression = function parseConditionalExpression ( contract , expression ) {
instrumenter . instrumentStatement ( contract , expression ) ;
instrumenter . instrumentConditionalExpression ( contract , expression ) ;
} ;
parse . ThrowStatement = function parseThrow Statement( contract , expression ) {
instrumenter . instrument Statement( contract , expression ) ;
parse . ContractStatement = function ParseContractStatement ( contract , expression ) {
parse . ContractOrLibraryStatement ( contract , expression ) ;
} ;
parse . IfStatement = function parseIfStatement ( contract , expression ) {
instrumenter . instrumentStatement ( contract , expression ) ;
instrumenter . instrumentIfStatement ( contract , expression ) ;
// We can't instrument
// if (x==1)
//
// So don't count x==1 as a statement - just the if as a whole.
// parse[expression.test.type](contract, expression.test)
parse [ expression . consequent . type ] ( contract , expression . consequent ) ;
if ( expression . alternate ) {
parse [ expression . alternate . type ] ( contract , expression . alternate ) ;
parse . ContractOrLibraryStatement = function parseContractOrLibraryStatement ( contract , expression ) {
// From the start of this contract statement, find the first '{', and inject there.
const injectionPoint = expression . start + contract . instrumented . slice ( expression . start ) . indexOf ( '{' ) + 2 ;
if ( contract . injectionPoints [ injectionPoint ] ) {
contract . injectionPoints [ expression . start + contract . instrumented . slice ( expression . start ) . indexOf ( '{' ) + 2 ] . push ( {
type : 'eventDefinition' ,
} ) ;
} else {
contract . injectionPoints [ expression . start + contract . instrumented . slice ( expression . start ) . indexOf ( '{' ) + 2 ] = [ {
type : 'eventDefinition' ,
} ] ;
}
if ( expression . body ) {
expression . body . forEach ( construct => {
parse [ construct . type ] &&
parse [ construct . type ] ( contract , construct ) ;
} ) ;
}
} ;
parse . ExpressionStatement = function parseExpressionStatement ( contract , content ) {
parse [ content . expression . type ] &&
parse [ content . expression . type ] ( contract , content . expression ) ;
} ;
parse . VariableDeclarationTuple = function parseVariableDeclarationTuple ( contract , expression ) {
parse [ expression . init . type ] ( contract , expression . init ) ;
} ;
parse . BlockStatement = function parseBlockStatement ( contract , expression ) {
for ( let x = 0 ; x < expression . body . length ; x ++ ) {
instrumenter . instrumentLine ( contract , expression . body [ x ] ) ;
parse [ expression . body [ x ] . type ] ( contract , expression . body [ x ] ) ;
}
} ;
parse . VariableDeclaration = function parseVariableDeclaration ( contract , expression ) {
parse . ForStatement = function parseForStatement ( contract , expression ) {
instrumenter . instrumentStatement ( contract , expression ) ;
if ( expression . declarations . length > 1 ) {
console . log ( 'more than one declaration' ) ;
}
parse [ expression . declarations [ 0 ] . id . type ] ( contract , expression . declarations [ 0 ] . id ) ;
} ;
parse . UsingStatement = function parseUsingStatement ( contract , expression ) {
parse [ expression . for . type ] ( contract , expression . for ) ;
parse [ expression . body . type ] &&
parse [ expression . body . type ] ( contract , expression . body ) ;
} ;
parse . FunctionDeclaration = function parseFunctionDeclaration ( contract , expression ) {
parse . Modifiers ( contract , expression . modifiers ) ;
if ( expression . body ) {
instrumenter . instrumentFunctionDeclaration ( contract , expression ) ;
parse [ expression . body . type ] &&
parse [ expression . body . type ] ( contract , expression . body ) ;
}
} ;
parse . ContractOrLibraryStatement = function parseContractOrLibraryStatement ( contract , expression ) {
// Inject our coverage event if we're covering
// This is harder because of where .start and .end represent, and how documented comments are validated
// by solc upon compilation. From the start of this contract statement, find the first '{', and inject
// there.
const injectionPoint = expression . start + contract . instrumented . slice ( expression . start ) . indexOf ( '{' ) + 2 ;
if ( contract . injectionPoints [ injectionPoint ] ) {
contract . injectionPoints [ expression . start + contract . instrumented . slice ( expression . start ) . indexOf ( '{' ) + 2 ] . push ( {
type : 'eventDefinition' ,
} ) ;
} else {
contract . injectionPoints [ expression . start + contract . instrumented . slice ( expression . start ) . indexOf ( '{' ) + 2 ] = [ {
type : 'eventDefinition' ,
} ] ;
}
parse . IfStatement = function parseIfStatement ( contract , expression ) {
instrumenter . instrumentStatement ( contract , expression ) ;
instrumenter . instrumentIfStatement ( contract , expression ) ;
if ( expression . body ) {
expression . body . forEach ( construct => {
parse [ construct . type ] ( contract , construct ) ;
} ) ;
parse [ expression . consequent . type ] &&
parse [ expression . consequent . type ] ( contract , expression . consequent ) ;
if ( expression . alternate ) {
parse [ expression . alternate . type ] &&
parse [ expression . alternate . type ] ( contract , expression . alternate ) ;
}
} ;
parse . ContractStatement = function ParseContract Statement( contract , expression ) {
parse . LibraryStatement = function parseLibraryStatement ( contract , expression ) {
parse . ContractOrLibraryStatement ( contract , expression ) ;
} ;
parse . LibraryStatement = function parseLibraryStatement ( contract , expression ) {
parse . ContractOrLibraryStatement ( contract , expression ) ;
parse . MemberExpression = function parseMemberExpression ( contract , expression ) {
parse [ expression . object . type ] &&
parse [ expression . object . type ] ( contract , expression . object ) ;
} ;
parse . Modifiers = function parseModifier ( contract , modifiers ) {
if ( modifiers ) {
modifiers . forEach ( modifier => {
parse [ modifier . type ] && parse [ modifier . type ] ( contract , modifier ) ;
} ) ;
}
} ;
parse . ModifierDeclaration = function parseModifierDeclaration ( contract , expression ) {
instrumenter . instrumentFunctionDeclaration ( contract , expression ) ;
parse [ expression . body . type ] &&
parse [ expression . body . type ] ( contract , expression . body ) ;
} ;
parse . NewExpression = function parseNewExpression ( contract , expression ) {
parse [ expression . callee . type ] &&
parse [ expression . callee . type ] ( contract , expression . callee ) ;
} ;
parse . Program = function parseProgram ( contract , expression ) {
expression . body . forEach ( construct => {
parse [ construct . type ] &&
parse [ construct . type ] ( contract , construct ) ;
} ) ;
} ;
parse . WhileStatement = function parseWhile Statement( contract , expression ) {
parse . ReturnStatement = function parseReturn Statement( contract , expression ) {
instrumenter . instrumentStatement ( contract , expression ) ;
parse [ expression . body . type ] ( contract , expression . body ) ;
} ;
parse . ForStatement = function parseForStatement ( contract , expression ) {
parse . ThrowStatement = function parseThrowStatement ( contract , expression ) {
instrumenter . instrumentStatement ( contract , expression ) ;
} ;
parse . UnaryExpression = function parseUnaryExpression ( contract , expression ) {
parse [ expression . argument . type ] &&
parse [ expression . argument . type ] ( contract , expression . argument ) ;
} ;
parse . UsingStatement = function parseUsingStatement ( contract , expression ) {
parse [ expression . for . type ] &&
parse [ expression . for . type ] ( contract , expression . for ) ;
} ;
parse . VariableDeclaration = function parseVariableDeclaration ( contract , expression ) {
instrumenter . instrumentStatement ( contract , expression ) ;
parse [ expression . declarations [ 0 ] . id . type ] &&
parse [ expression . declarations [ 0 ] . id . type ] ( contract , expression . declarations [ 0 ] . id ) ;
} ;
parse . VariableDeclarationTuple = function parseVariableDeclarationTuple ( contract , expression ) {
parse [ expression . init . type ] &&
parse [ expression . init . type ] ( contract , expression . init ) ;
} ;
parse . WhileStatement = function parseWhileStatement ( contract , expression ) {
instrumenter . instrumentStatement ( contract , expression ) ;
parse [ expression . body . type ] &&
parse [ expression . body . type ] ( contract , expression . body ) ;
} ;