Begin transition to solidity-parser-antlr

pull/319/head
Alex 6 years ago
parent cf95ea1b65
commit 95b099afbf
  1. 13
      lib/instrumentSolidity.js
  2. 78
      lib/instrumenter.js
  3. 75
      lib/parse.js
  4. 5
      package-lock.json
  5. 1
      package.json
  6. 2
      test/function.js
  7. 4
      test/sources/statements/tuple.sol
  8. 2
      test/statements.js

@ -1,4 +1,5 @@
const SolidityParser = require('solidity-parser-sc');
const SolidityParserAntlr = require('solidity-parser-antlr');
const preprocessor = require('./preprocessor');
const injector = require('./injector');
const parse = require('./parse');
@ -20,7 +21,7 @@ module.exports = function instrumentSolidity(contractSource, fileName) {
contract.injectionPoints = {};
// First, we run over the original contract to get the source mapping.
let ast = SolidityParser.parse(contract.source);
let ast = SolidityParserAntlr.parse(contract.source, {range: true});
parse[ast.type](contract, ast);
const retValue = JSON.parse(JSON.stringify(contract));
@ -36,12 +37,10 @@ module.exports = function instrumentSolidity(contractSource, fileName) {
contract.preprocessed = preprocessor.run(contract.source);
contract.instrumented = contract.preprocessed;
ast = SolidityParser.parse(contract.preprocessed);
const contractStatement = ast.body.filter(node => (node.type === 'ContractStatement' ||
node.type === 'LibraryStatement' ||
node.type === 'InterfaceStatement'));
ast = SolidityParserAntlr.parse(contract.preprocessed, {range: true});
const contractStatement = ast.children.filter(node => (node.type === 'ContractDefinition' ||
node.type === 'LibraryDefinition' ||
node.type === 'InterfaceDefinition'));
contract.contractName = contractStatement[0].name;
parse[ast.type](contract, ast);

@ -14,7 +14,7 @@ function createOrAppendInjectionPoint(contract, key, value) {
instrumenter.prePosition = function prePosition(expression) {
if (expression.right.type === 'ConditionalExpression' &&
expression.left.type === 'MemberExpression') {
expression.start -= 2;
expression.range[0] -= 2;
}
};
@ -31,15 +31,15 @@ instrumenter.instrumentAssignmentExpression = function instrumentAssignmentExpre
if (expression.left.type === 'DeclarativeExpression' || expression.left.type === 'Identifier') {
// Then we need to go from bytes32 varname = (conditional expression)
// to bytes32 varname; (,varname) = (conditional expression)
createOrAppendInjectionPoint(contract, expression.left.end, {
createOrAppendInjectionPoint(contract, expression.left.range[1], {
type: 'literal', string: '; (,' + expression.left.name + ')',
});
instrumenter.instrumentConditionalExpression(contract, expression.right);
} else if (expression.left.type === 'MemberExpression') {
createOrAppendInjectionPoint(contract, expression.left.start, {
createOrAppendInjectionPoint(contract, expression.left.range[0], {
type: 'literal', string: '(,',
});
createOrAppendInjectionPoint(contract, expression.left.end, {
createOrAppendInjectionPoint(contract, expression.left.range[1], {
type: 'literal', string: ')',
});
instrumenter.instrumentConditionalExpression(contract, expression.right);
@ -61,12 +61,12 @@ instrumenter.instrumentConditionalExpression = function instrumentConditionalExp
/*contract.branchId += 1;
const startline = (contract.instrumented.slice(0, expression.start).match(/\n/g) || []).length + 1;
const startcol = expression.start - contract.instrumented.slice(0, expression.start).lastIndexOf('\n') - 1;
const consequentStartCol = startcol + (contract, expression.consequent.start - expression.start);
const consequentEndCol = consequentStartCol + (contract, expression.consequent.end - expression.consequent.start);
const alternateStartCol = startcol + (contract, expression.alternate.start - expression.start);
const alternateEndCol = alternateStartCol + (contract, expression.alternate.end - expression.alternate.start);
const startline = (contract.instrumented.slice(0, expression.range[0]).match(/\n/g) || []).length + 1;
const startcol = expression.range[0] - contract.instrumented.slice(0, expression.range[0]).lastIndexOf('\n') - 1;
const consequentStartCol = startcol + (contract, expression.trueBody.range[0] - expression.range[0]);
const consequentEndCol = consequentStartCol + (contract, expression.trueBody.range[1] - expression.trueBody.range[0]);
const alternateStartCol = startcol + (contract, expression.falseBody.range[0] - expression.range[0]);
const alternateEndCol = alternateStartCol + (contract, expression.falseBody.range[1] - expression.falseBody.range[0]);
// NB locations for conditional branches in istanbul are length 1 and associated with the : and ?.
contract.branchMap[contract.branchId] = {
line: startline,
@ -95,24 +95,24 @@ instrumenter.instrumentConditionalExpression = function instrumentConditionalExp
// Wrap the consequent
createOrAppendInjectionPoint(contract, expression.consequent.start, {
createOrAppendInjectionPoint(contract, expression.trueBody.range[0], {
type: 'openParen',
});
createOrAppendInjectionPoint(contract, expression.consequent.start, {
createOrAppendInjectionPoint(contract, expression.trueBody.range[0], {
type: 'callBranchEvent', comma: true, branchId: contract.branchId, locationIdx: 0,
});
createOrAppendInjectionPoint(contract, expression.consequent.end, {
createOrAppendInjectionPoint(contract, expression.trueBody.range[1], {
type: 'closeParen',
});
// Wrap the alternate
createOrAppendInjectionPoint(contract, expression.alternate.start, {
createOrAppendInjectionPoint(contract, expression.falseBody.range[0], {
type: 'openParen',
});
createOrAppendInjectionPoint(contract, expression.alternate.start, {
createOrAppendInjectionPoint(contract, expression.falseBody.range[0], {
type: 'callBranchEvent', comma: true, branchId: contract.branchId, locationIdx: 1,
});
createOrAppendInjectionPoint(contract, expression.alternate.end, {
createOrAppendInjectionPoint(contract, expression.falseBody.range[1], {
type: 'closeParen',
});*/
};
@ -120,14 +120,14 @@ instrumenter.instrumentConditionalExpression = function instrumentConditionalExp
instrumenter.instrumentStatement = function instrumentStatement(contract, expression) {
contract.statementId += 1;
// We need to work out the lines and columns the expression starts and ends
const startline = (contract.instrumented.slice(0, expression.start).match(/\n/g) || []).length + 1;
const startcol = expression.start - contract.instrumented.slice(0, expression.start).lastIndexOf('\n') - 1;
const expressionContent = contract.instrumented.slice(expression.start, expression.end);
const startline = (contract.instrumented.slice(0, expression.range[0]).match(/\n/g) || []).length + 1;
const startcol = expression.range[0] - contract.instrumented.slice(0, expression.range[0]).lastIndexOf('\n') - 1;
const expressionContent = contract.instrumented.slice(expression.range[0], expression.range[1] + 1);
const endline = startline + (contract, expressionContent.match('/\n/g') || []).length;
let endcol;
if (expressionContent.lastIndexOf('\n') >= 0) {
endcol = contract.instrumented.slice(expressionContent.lastIndexOf('\n'), expression.end).length - 1;
endcol = contract.instrumented.slice(expressionContent.lastIndexOf('\n'), expression.range[1]).length;
} else {
endcol = startcol + (contract, expressionContent.length - 1);
}
@ -139,15 +139,15 @@ instrumenter.instrumentStatement = function instrumentStatement(contract, expres
line: endline, column: endcol,
},
};
createOrAppendInjectionPoint(contract, expression.start, {
createOrAppendInjectionPoint(contract, expression.range[0], {
type: 'statement', statementId: contract.statementId,
});
};
instrumenter.instrumentLine = function instrumentLine(contract, expression) {
// what's the position of the most recent newline?
const startchar = expression.start;
const endchar = expression.end;
const startchar = expression.range[0];
const endchar = expression.range[1] + 1;
const lastNewLine = contract.instrumented.slice(0, startchar).lastIndexOf('\n');
const nextNewLine = startchar + contract.instrumented.slice(startchar).indexOf('\n');
const contractSnipped = contract.instrumented.slice(lastNewLine, nextNewLine);
@ -160,7 +160,7 @@ instrumenter.instrumentLine = function instrumentLine(contract, expression) {
});
} else if (contract.instrumented.slice(lastNewLine, startchar).replace('{', '').trim().length === 0 &&
contract.instrumented.slice(endchar, nextNewLine).replace(/[;}]/g, '').trim().length === 0) {
createOrAppendInjectionPoint(contract, expression.start, {
createOrAppendInjectionPoint(contract, expression.range[0], {
type: 'callEvent',
});
}
@ -169,11 +169,11 @@ instrumenter.instrumentLine = function instrumentLine(contract, expression) {
instrumenter.instrumentFunctionDeclaration = function instrumentFunctionDeclaration(contract, expression) {
contract.fnId += 1;
const startline = (contract.instrumented.slice(0, expression.start).match(/\n/g) || []).length + 1;
const startline = (contract.instrumented.slice(0, expression.range[0]).match(/\n/g) || []).length + 1;
// We need to work out the lines and columns the function declaration starts and ends
const startcol = expression.start - contract.instrumented.slice(0, expression.start).lastIndexOf('\n') - 1;
const endlineDelta = contract.instrumented.slice(expression.start).indexOf('{');
const functionDefinition = contract.instrumented.slice(expression.start, expression.start + endlineDelta);
const startcol = expression.range[0] - contract.instrumented.slice(0, expression.range[0]).lastIndexOf('\n') - 1;
const endlineDelta = contract.instrumented.slice(expression.range[0]).indexOf('{');
const functionDefinition = contract.instrumented.slice(expression.range[0], expression.range[0] + endlineDelta);
const endline = startline + (functionDefinition.match(/\n/g) || []).length;
const endcol = functionDefinition.length - functionDefinition.lastIndexOf('\n');
contract.fnMap[contract.fnId] = {
@ -188,15 +188,15 @@ instrumenter.instrumentFunctionDeclaration = function instrumentFunctionDeclarat
},
},
};
createOrAppendInjectionPoint(contract, expression.start + endlineDelta + 1, {
createOrAppendInjectionPoint(contract, expression.range[0] + endlineDelta + 1, {
type: 'callFunctionEvent', fnId: contract.fnId,
});
};
instrumenter.addNewBranch = function addNewBranch(contract, expression) {
contract.branchId += 1;
const startline = (contract.instrumented.slice(0, expression.start).match(/\n/g) || []).length + 1;
const startcol = expression.start - contract.instrumented.slice(0, expression.start).lastIndexOf('\n') - 1;
const startline = (contract.instrumented.slice(0, expression.range[0]).match(/\n/g) || []).length + 1;
const startcol = expression.range[0] - contract.instrumented.slice(0, expression.range[0]).lastIndexOf('\n') - 1;
// NB locations for if branches in istanbul are zero length and associated with the start of the if.
contract.branchMap[contract.branchId] = {
line: startline,
@ -221,31 +221,31 @@ instrumenter.addNewBranch = function addNewBranch(contract, expression) {
instrumenter.instrumentAssertOrRequire = function instrumentAssertOrRequire(contract, expression) {
instrumenter.addNewBranch(contract, expression);
createOrAppendInjectionPoint(contract, expression.start, {
createOrAppendInjectionPoint(contract, expression.range[0], {
type: 'callAssertPreEvent', branchId: contract.branchId,
});
createOrAppendInjectionPoint(contract, expression.end + 1, {
createOrAppendInjectionPoint(contract, expression.range[1] + 2, {
type: 'callAssertPostEvent', branchId: contract.branchId,
});
};
instrumenter.instrumentIfStatement = function instrumentIfStatement(contract, expression) {
instrumenter.addNewBranch(contract, expression);
if (expression.consequent.type === 'BlockStatement') {
createOrAppendInjectionPoint(contract, expression.consequent.start + 1, {
if (expression.trueBody.type === 'Block') {
createOrAppendInjectionPoint(contract, expression.trueBody.range[0] + 1, {
type: 'callBranchEvent', branchId: contract.branchId, locationIdx: 0,
});
}
if (expression.alternate && expression.alternate.type === 'IfStatement') {
if (expression.falseBody && expression.falseBody.type === 'IfStatement') {
// Do nothing - we must be pre-preprocessor, so don't bother instrumenting -
// when we're actually instrumenting, this will never happen (we've wrapped it in
// a block statement)
} else if (expression.alternate && expression.alternate.type === 'BlockStatement') {
createOrAppendInjectionPoint(contract, expression.alternate.start + 1, {
} else if (expression.falseBody && expression.falseBody.type === 'Block') {
createOrAppendInjectionPoint(contract, expression.falseBody.range[0] + 1, {
type: 'callBranchEvent', branchId: contract.branchId, locationIdx: 1,
});
} else {
createOrAppendInjectionPoint(contract, expression.consequent.end, {
createOrAppendInjectionPoint(contract, expression.trueBody.range[1] + 1, {
type: 'callEmptyBranchEvent', branchId: contract.branchId, locationIdx: 1,
});
}

@ -15,26 +15,31 @@ parse.AssignmentExpression = function parseAssignmentExpression(contract, expres
instrumenter.instrumentAssignmentExpression(contract, expression);
};
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.Block = function parseBlock(contract, expression) {
for (let x = 0; x < expression.statements.length; x++) {
instrumenter.instrumentLine(contract, expression.statements[x]);
parse[expression.statements[x].type] &&
parse[expression.statements[x].type](contract, expression.statements[x]);
}
};
parse.CallExpression = function parseCallExpression(contract, expression) {
// In any given chain of call expressions, only the head callee is an Identifier node
if (expression.callee.type === 'Identifier') {
parse.BinaryOperation = function parseBinaryOperation(contract, expression) {
instrumenter.instrumentStatement(contract, expression);
}
parse.FunctionCall = function parseCallExpression(contract, expression) {
// In any given chain of call expressions, only the last one will fail this check. This makes sure
// we don't instrument a chain of expressions multiple times.
if (expression.expression.type !== 'FunctionCall') {
instrumenter.instrumentStatement(contract, expression);
if (expression.callee.name === 'assert' || expression.callee.name === 'require') {
if (expression.expression.name === 'assert' || expression.expression.name === 'require') {
instrumenter.instrumentAssertOrRequire(contract, expression);
}
parse[expression.callee.type] &&
parse[expression.callee.type](contract, expression.callee);
parse[expression.expression.type] &&
parse[expression.expression.type](contract, expression.expression);
} else {
parse[expression.callee.type] &&
parse[expression.callee.type](contract, expression.callee);
parse[expression.expression.type] &&
parse[expression.expression.type](contract, expression.expression);
}
};
@ -43,25 +48,25 @@ parse.ConditionalExpression = function parseConditionalExpression(contract, expr
instrumenter.instrumentConditionalExpression(contract, expression);
};
parse.ContractStatement = function ParseContractStatement(contract, expression) {
parse.ContractDefinition = function ParseContractStatement(contract, expression) {
parse.ContractOrLibraryStatement(contract, expression);
};
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('{') + 1;
const injectionPoint = expression.range[0] + contract.instrumented.slice(expression.range[0]).indexOf('{') + 1;
if (contract.injectionPoints[injectionPoint]) {
contract.injectionPoints[expression.start + contract.instrumented.slice(expression.start).indexOf('{') + 1].push({
contract.injectionPoints[expression.range[0] + contract.instrumented.slice(expression.range[0]).indexOf('{') + 1].push({
type: 'eventDefinition',
});
} else {
contract.injectionPoints[expression.start + contract.instrumented.slice(expression.start).indexOf('{') + 1] = [{
contract.injectionPoints[expression.range[0] + contract.instrumented.slice(expression.range[0]).indexOf('{') + 1] = [{
type: 'eventDefinition',
}];
}
if (expression.body) {
expression.body.forEach(construct => {
if (expression.subNodes) {
expression.subNodes.forEach(construct => {
parse[construct.type] &&
parse[construct.type](contract, construct);
});
@ -83,11 +88,10 @@ parse.ForStatement = function parseForStatement(contract, expression) {
parse[expression.body.type](contract, expression.body);
};
parse.FunctionDeclaration = function parseFunctionDeclaration(contract, expression) {
parse.FunctionDefinition = function parseFunctionDefinition(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);
}
@ -96,13 +100,12 @@ parse.FunctionDeclaration = function parseFunctionDeclaration(contract, expressi
parse.IfStatement = function parseIfStatement(contract, expression) {
instrumenter.instrumentStatement(contract, expression);
instrumenter.instrumentIfStatement(contract, expression);
parse[expression.trueBody.type] &&
parse[expression.trueBody.type](contract, expression.trueBody);
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);
if (expression.falseBody) {
parse[expression.falseBody.type] &&
parse[expression.falseBody.type](contract, expression.falseBody);
}
};
@ -134,12 +137,12 @@ parse.ModifierDeclaration = function parseModifierDeclaration(contract, expressi
};
parse.NewExpression = function parseNewExpression(contract, expression) {
parse[expression.callee.type] &&
parse[expression.callee.type](contract, expression.callee);
parse[expression.typeName.type] &&
parse[expression.typeName.type](contract, expression.typeName);
};
parse.Program = function parseProgram(contract, expression) {
expression.body.forEach(construct => {
parse.SourceUnit = function parseSourceUnit(contract, expression) {
expression.children.forEach(construct => {
parse[construct.type] &&
parse[construct.type](contract, construct);
});
@ -149,10 +152,6 @@ parse.ReturnStatement = function parseReturnStatement(contract, expression) {
instrumenter.instrumentStatement(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);
@ -163,10 +162,10 @@ parse.UsingStatement = function parseUsingStatement(contract, expression) {
parse[expression.for.type](contract, expression.for);
};
parse.VariableDeclaration = function parseVariableDeclaration(contract, expression) {
parse.VariableDeclarationStatement = function parseVariableDeclarationStatement(contract, expression) {
instrumenter.instrumentStatement(contract, expression);
parse[expression.declarations[0].id.type] &&
parse[expression.declarations[0].id.type](contract, expression.declarations[0].id);
// parse[expression.declarations[0].id.type] &&
// parse[expression.declarations[0].id.type](contract, expression.declarations[0].id);
};
parse.VariableDeclarationTuple = function parseVariableDeclarationTuple(contract, expression) {

5
package-lock.json generated

@ -4247,6 +4247,11 @@
}
}
},
"solidity-parser-antlr": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/solidity-parser-antlr/-/solidity-parser-antlr-0.4.1.tgz",
"integrity": "sha512-3lXZd3kUbCyiLCieDeyhAm2jPtPXKQYVR5wItbVLRw4MW4fv5pSGC3v4x6BRucKVE6tjVZ9AhTErkqRu2KlXDg=="
},
"solidity-parser-sc": {
"version": "git+https://github.com/sc-forks/solidity-parser.git#898896cff7d22a729e5af3bc32f53a72edb1f1bf",
"from": "git+https://github.com/sc-forks/solidity-parser.git#leapdao",

@ -28,6 +28,7 @@
"req-cwd": "^1.0.1",
"shelljs": "^0.7.4",
"sol-explore": "^1.6.2",
"solidity-parser-antlr": "^0.4.1",
"solidity-parser-sc": "git+https://github.com/sc-forks/solidity-parser.git#leapdao",
"tree-kill": "^1.2.0",
"web3": "^1.0.0-beta.37",

@ -102,7 +102,7 @@ describe('function declarations', () => {
});
assert.deepEqual(mapping[filePath].b, {});
assert.deepEqual(mapping[filePath].s, {
1: 1,
1: 1, 2: 1
});
assert.deepEqual(mapping[filePath].f, {
1: 1,

@ -7,9 +7,7 @@ contract Test {
}
function a() public {
uint a;
uint b;
(a, b) = (10, 20);
(uint a, uint b) = (10, 20);
(a, b) = returnTuple();
}
}

@ -157,7 +157,7 @@ describe('generic statements', () => {
vm.execute(info.contract, 'a', []).then(events => {
const mapping = coverage.generate(events, pathPrefix);
assert.deepEqual(mapping[filePath].l, {
6: 1, 10: 1, 11: 1, 12: 1, 13: 1
6: 1, 10: 1, 11: 1
});
assert.deepEqual(mapping[filePath].b, {});
assert.deepEqual(mapping[filePath].s, {

Loading…
Cancel
Save