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

93 lines
3.4 KiB

const SolExplore = require('sol-explore');
const SolidityParser = require('solidity-parser-sc');
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.start) + '{' + contract.slice(expression.start, expression.end) + '}' + contract.slice(expression.end);
}
/**
* Captures carriage returns at modifiers we'll remove. These need to be re-injected into the
* source to keep line report alignments accurate.
* @param {String} contract solidity source
* @param {Object} modifier AST node
* @return {String} whitespace around the modifier
*/
function getModifierWhitespace(contract, modifier){
const source = contract.slice(modifier.start, modifier.end);
const whitespace = source.match(crRegex) || [];
return whitespace.join('');
}
/**
* 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);
keepRunning = false;
SolExplore.traverse(ast, {
enter(node, parent) { // eslint-disable-line no-loop-func
// If consequents
if (node.type === 'IfStatement' && node.consequent.type !== 'BlockStatement') {
contract = blockWrap(contract, node.consequent);
keepRunning = true;
this.stopTraversal();
// If alternates
} else if (
node.type === 'IfStatement' &&
node.alternate &&
node.alternate.type !== 'BlockStatement') {
contract = blockWrap(contract, node.alternate);
keepRunning = true;
this.stopTraversal();
// Loops
} else if (
(node.type === 'ForStatement' || node.type === 'WhileStatement') &&
node.body.type !== 'BlockStatement') {
contract = blockWrap(contract, node.body);
keepRunning = true;
this.stopTraversal();
} else if (node.type === 'FunctionDeclaration' && node.modifiers) {
// We want to remove constant / pure / view from functions
for (let i = 0; i < node.modifiers.length; i++) {
if (['pure', 'constant', 'view'].indexOf(node.modifiers[i].name) > -1) {
let whitespace = getModifierWhitespace(contract, node.modifiers[i]);
contract = contract.slice(0, node.modifiers[i].start) +
whitespace +
contract.slice(node.modifiers[i].end);
keepRunning = true;
this.stopTraversal();
}
}
}
},
});
} catch (err) {
contract = err;
keepRunning = false;
}
}
return contract;
};