|
|
@ -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 parse = {}; |
|
|
|
const instrumenter = require('./instrumenter'); |
|
|
|
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) { |
|
|
|
parse.AssignmentExpression = function parseAssignmentExpression(contract, expression) { |
|
|
|
instrumenter.instrumentStatement(contract, expression); |
|
|
|
instrumenter.instrumentStatement(contract, expression); |
|
|
|
instrumenter.instrumentAssignmentExpression(contract, expression); |
|
|
|
instrumenter.instrumentAssignmentExpression(contract, expression); |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
parse.ConditionalExpression = function parseConditionalExpression(contract, expression) { |
|
|
|
parse.BlockStatement = function parseBlockStatement(contract, expression) { |
|
|
|
instrumenter.instrumentStatement(contract, expression); |
|
|
|
for (let x = 0; x < expression.body.length; x++) { |
|
|
|
instrumenter.instrumentConditionalExpression(contract, expression); |
|
|
|
instrumenter.instrumentLine(contract, expression.body[x]); |
|
|
|
}; |
|
|
|
parse[expression.body[x].type] && |
|
|
|
|
|
|
|
parse[expression.body[x].type](contract, expression.body[x]); |
|
|
|
parse.Modifiers = function parseModifier(contract, modifiers) { |
|
|
|
|
|
|
|
if (modifiers) { |
|
|
|
|
|
|
|
modifiers.forEach(modifier => { |
|
|
|
|
|
|
|
parse[modifier.type](contract, modifier); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
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) { |
|
|
|
parse.CallExpression = function parseCallExpression(contract, expression) { |
|
|
|
// It looks like in any given chain of call expressions, only the head callee is an Identifier
|
|
|
|
// In any given chain of call expressions, only the head callee is an Identifier node
|
|
|
|
// node ... and we only want to instrument the statement once.
|
|
|
|
|
|
|
|
if (expression.callee.type === 'Identifier') { |
|
|
|
if (expression.callee.type === 'Identifier') { |
|
|
|
instrumenter.instrumentStatement(contract, expression); |
|
|
|
instrumenter.instrumentStatement(contract, expression); |
|
|
|
|
|
|
|
parse[expression.callee.type] && |
|
|
|
parse[expression.callee.type](contract, expression.callee); |
|
|
|
parse[expression.callee.type](contract, expression.callee); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
|
|
|
|
parse[expression.callee.type] && |
|
|
|
parse[expression.callee.type](contract, expression.callee); |
|
|
|
parse[expression.callee.type](contract, expression.callee); |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
parse.UnaryExpression = function parseUnaryExpression(contract, expression) { |
|
|
|
parse.ConditionalExpression = function parseConditionalExpression(contract, expression) { |
|
|
|
parse[expression.argument.type](contract, expression.argument); |
|
|
|
instrumenter.instrumentStatement(contract, expression); |
|
|
|
|
|
|
|
instrumenter.instrumentConditionalExpression(contract, expression); |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
parse.ThrowStatement = function parseThrowStatement(contract, expression) { |
|
|
|
parse.ContractStatement = function ParseContractStatement(contract, expression) { |
|
|
|
instrumenter.instrumentStatement(contract, expression); |
|
|
|
parse.ContractOrLibraryStatement(contract, expression); |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
parse.IfStatement = function parseIfStatement(contract, expression) { |
|
|
|
parse.ContractOrLibraryStatement = function parseContractOrLibraryStatement(contract, expression) { |
|
|
|
instrumenter.instrumentStatement(contract, expression); |
|
|
|
// From the start of this contract statement, find the first '{', and inject there.
|
|
|
|
instrumenter.instrumentIfStatement(contract, expression); |
|
|
|
const injectionPoint = expression.start + contract.instrumented.slice(expression.start).indexOf('{') + 2; |
|
|
|
// We can't instrument
|
|
|
|
if (contract.injectionPoints[injectionPoint]) { |
|
|
|
// if (x==1)
|
|
|
|
contract.injectionPoints[expression.start + contract.instrumented.slice(expression.start).indexOf('{') + 2].push({ |
|
|
|
//
|
|
|
|
type: 'eventDefinition', |
|
|
|
// So don't count x==1 as a statement - just the if as a whole.
|
|
|
|
}); |
|
|
|
// parse[expression.test.type](contract, expression.test)
|
|
|
|
} else { |
|
|
|
parse[expression.consequent.type](contract, expression.consequent); |
|
|
|
contract.injectionPoints[expression.start + contract.instrumented.slice(expression.start).indexOf('{') + 2] = [{ |
|
|
|
if (expression.alternate) { |
|
|
|
type: 'eventDefinition', |
|
|
|
parse[expression.alternate.type](contract, expression.alternate); |
|
|
|
}]; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (expression.body) { |
|
|
|
|
|
|
|
expression.body.forEach(construct => { |
|
|
|
|
|
|
|
parse[construct.type] && |
|
|
|
|
|
|
|
parse[construct.type](contract, construct); |
|
|
|
|
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
parse.ExpressionStatement = function parseExpressionStatement(contract, content) { |
|
|
|
parse.ExpressionStatement = function parseExpressionStatement(contract, content) { |
|
|
|
|
|
|
|
parse[content.expression.type] && |
|
|
|
parse[content.expression.type](contract, content.expression); |
|
|
|
parse[content.expression.type](contract, content.expression); |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
parse.VariableDeclarationTuple = function parseVariableDeclarationTuple(contract, expression) { |
|
|
|
parse.ForStatement = function parseForStatement(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) { |
|
|
|
|
|
|
|
instrumenter.instrumentStatement(contract, expression); |
|
|
|
instrumenter.instrumentStatement(contract, expression); |
|
|
|
if (expression.declarations.length > 1) { |
|
|
|
parse[expression.body.type] && |
|
|
|
console.log('more than one declaration'); |
|
|
|
parse[expression.body.type](contract, expression.body); |
|
|
|
} |
|
|
|
|
|
|
|
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.FunctionDeclaration = function parseFunctionDeclaration(contract, expression) { |
|
|
|
parse.FunctionDeclaration = function parseFunctionDeclaration(contract, expression) { |
|
|
|
parse.Modifiers(contract, expression.modifiers); |
|
|
|
parse.Modifiers(contract, expression.modifiers); |
|
|
|
if (expression.body) { |
|
|
|
if (expression.body) { |
|
|
|
instrumenter.instrumentFunctionDeclaration(contract, expression); |
|
|
|
instrumenter.instrumentFunctionDeclaration(contract, expression); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
parse[expression.body.type] && |
|
|
|
parse[expression.body.type](contract, expression.body); |
|
|
|
parse[expression.body.type](contract, expression.body); |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
parse.ContractOrLibraryStatement = function parseContractOrLibraryStatement(contract, expression) { |
|
|
|
parse.IfStatement = function parseIfStatement(contract, expression) { |
|
|
|
// Inject our coverage event if we're covering
|
|
|
|
instrumenter.instrumentStatement(contract, expression); |
|
|
|
// This is harder because of where .start and .end represent, and how documented comments are validated
|
|
|
|
instrumenter.instrumentIfStatement(contract, expression); |
|
|
|
// 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', |
|
|
|
|
|
|
|
}]; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (expression.body) { |
|
|
|
parse[expression.consequent.type] && |
|
|
|
expression.body.forEach(construct => { |
|
|
|
parse[expression.consequent.type](contract, expression.consequent); |
|
|
|
parse[construct.type](contract, construct); |
|
|
|
|
|
|
|
}); |
|
|
|
if (expression.alternate) { |
|
|
|
|
|
|
|
parse[expression.alternate.type] && |
|
|
|
|
|
|
|
parse[expression.alternate.type](contract, expression.alternate); |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
parse.ContractStatement = function ParseContractStatement(contract, expression) { |
|
|
|
parse.LibraryStatement = function parseLibraryStatement(contract, expression) { |
|
|
|
parse.ContractOrLibraryStatement(contract, expression); |
|
|
|
parse.ContractOrLibraryStatement(contract, expression); |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
parse.LibraryStatement = function parseLibraryStatement(contract, expression) { |
|
|
|
parse.MemberExpression = function parseMemberExpression(contract, expression) { |
|
|
|
parse.ContractOrLibraryStatement(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) { |
|
|
|
parse.ModifierDeclaration = function parseModifierDeclaration(contract, expression) { |
|
|
|
instrumenter.instrumentFunctionDeclaration(contract, expression); |
|
|
|
instrumenter.instrumentFunctionDeclaration(contract, expression); |
|
|
|
|
|
|
|
parse[expression.body.type] && |
|
|
|
parse[expression.body.type](contract, expression.body); |
|
|
|
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) { |
|
|
|
parse.Program = function parseProgram(contract, expression) { |
|
|
|
expression.body.forEach(construct => { |
|
|
|
expression.body.forEach(construct => { |
|
|
|
|
|
|
|
parse[construct.type] && |
|
|
|
parse[construct.type](contract, construct); |
|
|
|
parse[construct.type](contract, construct); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
parse.WhileStatement = function parseWhileStatement(contract, expression) { |
|
|
|
parse.ReturnStatement = function parseReturnStatement(contract, expression) { |
|
|
|
instrumenter.instrumentStatement(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); |
|
|
|
instrumenter.instrumentStatement(contract, expression); |
|
|
|
|
|
|
|
parse[expression.body.type] && |
|
|
|
parse[expression.body.type](contract, expression.body); |
|
|
|
parse[expression.body.type](contract, expression.body); |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|