Code coverage for Solidity smart-contracts
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
solidity-coverage/lib/preprocessor.js

100 lines
3.5 KiB

const SolExplore = require('sol-explore');
const SolidityParser = require('solidity-parser-antlr');
const crRegex = /[\r\n ]+$/g;
/**
* Splices enclosing brackets into `contract` around `expression`;
* @param {String} contract solidity source
* @param {Object} node AST node to bracket
* @return {String} contract
*/
function blockWrap(contract, expression) {
return contract.slice(0, expression.range[0]) + '{' + contract.slice(expression.range[0], expression.range[1] + 1) + '}' + contract.slice(expression.range[1] + 1);
}
/** Remove 'pure' and 'view' from the function declaration.
* @param {String} contract solidity source
* @param {Object} function AST node
* @return {String} contract with the modifiers removed from the given function.
*/
function removePureView(contract, node){
let fDefStart = node.range[0];
if (node.body){
fDefEnd = node.body.range[0];
} else if (node.returnParameters) {
fDefEnd = node.returnParameters.range[0];
} else {
fDefEnd = node.range[1];
}
let fDef = contract.slice(fDefStart, fDefEnd + 1);
fDef = fDef.replace(/\bview\b/i, ' ');
fDef = fDef.replace(/\bpure\b/i, ' ');
return contract.slice(0, fDefStart) + fDef + contract.slice(fDefEnd + 1);
}
/**
* Locates unbracketed singleton statements attached to if, else, for and while statements
* and brackets them. Instrumenter needs to inject events at these locations and having
* them pre-bracketed simplifies the process. Each time a modification is made the contract
* is passed back to the parser and re-walked because all the starts and ends get shifted.
*
* Also removes pure and view modifiers.
*
* @param {String} contract solidity code
* @return {String} contract
*/
module.exports.run = function r(contract) {
let keepRunning = true;
while (keepRunning) {
try {
const ast = SolidityParser.parse(contract, { range: true });
keepRunning = false;
blocksToWrap = [];
SolidityParser.visit(ast, {
IfStatement: function(node) {
if (node.trueBody.type !== 'Block') {
blocksToWrap.push(node.trueBody);
keepRunning = true;
return false;
} else if (node.falseBody && node.falseBody.type !== 'Block'){
blocksToWrap.push(node.falseBody);
keepRunning = true;
return false;
}
},
ForStatement: function(node){
if (node.body.type !== 'Block'){
blocksToWrap.push(node.body);
keepRunning = true;
return false;
}
},
WhileStatement: function(node){
if (node.body.type !== 'Block'){
blocksToWrap.push(node.body);
keepRunning = true;
return false;
}
},
FunctionDefinition: function(node){
if (node.stateMutability === 'view' || node.stateMutability === 'pure'){
contract = removePureView(contract, node);
keepRunning = true;
return false;
}
}
})
// Returning 'false' only stops going any deeper in that particular branch of the tree, but other branches
// continue.
// So we apply the blocks we found in reverse order to avoid extra characters messing things up.
blocksToWrap.sort((a,b) => a.range[0] < b.range[0]);
blocksToWrap.forEach(block => contract = blockWrap(contract, block))
} catch (err) {
contract = err;
keepRunning = false;
}
}
return contract;
};