diff --git a/lib/parse.js b/lib/parse.js index 805d869..8eb83da 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -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 parseThrowStatement(contract, expression) { - instrumenter.instrumentStatement(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); + + parse[expression.consequent.type] && + parse[expression.consequent.type](contract, expression.consequent); - if (expression.body) { - expression.body.forEach(construct => { - 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.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 parseWhileStatement(contract, expression) { +parse.ReturnStatement = function parseReturnStatement(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); };