|
|
|
const SolExplore = require('sol-explore');
|
|
|
|
const SolidityParser = require('solidity-parser-sc');
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Splices enclosing brackets into `contract` around `expression`;
|
|
|
|
* @param {String} contract solidity source
|
|
|
|
* @param {Object} expression 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Parses the AST tree to remove pure, constant and view modifiers
|
|
|
|
* @param {Object} ast AST tree
|
|
|
|
* @param {String} contract solidity code
|
|
|
|
* @return {String} contract
|
|
|
|
*/
|
|
|
|
function removeViewPureConst(ast, contract) {
|
|
|
|
SolExplore.traverse(ast, {
|
|
|
|
enter(node, parent) {
|
|
|
|
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) {
|
|
|
|
contract = contract.slice(0, node.modifiers[i].start) +
|
|
|
|
' '.repeat(node.modifiers[i].end - node.modifiers[i].start) +
|
|
|
|
contract.slice(node.modifiers[i].end);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
});
|
|
|
|
return contract;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Parses the AST tree to locate unbracketed singleton statements attached to if, else, for and while statements
|
|
|
|
* and brackets them
|
|
|
|
* @param {Object} ast AST tree product of SolidityParser
|
|
|
|
* @param {String} contract solidity code
|
|
|
|
* @return {String} contract
|
|
|
|
*/
|
|
|
|
function bracketUnbStatements(ast, contract) {
|
|
|
|
const brkList = [];
|
|
|
|
let offset = 0;
|
|
|
|
SolExplore.traverse(ast, {
|
|
|
|
enter(node, parent) { // eslint-disable-line no-loop-func
|
|
|
|
// If consequents
|
|
|
|
if (node.type === 'IfStatement' && node.consequent.type !== 'BlockStatement') {
|
|
|
|
brkList.push({
|
|
|
|
start: node.consequent.start, end: node.consequent.end,
|
|
|
|
});
|
|
|
|
// If alternates
|
|
|
|
} else if (
|
|
|
|
node.type === 'IfStatement' &&
|
|
|
|
node.alternate &&
|
|
|
|
node.alternate.type !== 'BlockStatement') {
|
|
|
|
brkList.push({
|
|
|
|
start: node.alternate.start, end: node.alternate.end,
|
|
|
|
});
|
|
|
|
// Loops
|
|
|
|
} else if (
|
|
|
|
(node.type === 'ForStatement' || node.type === 'WhileStatement') &&
|
|
|
|
node.body.type !== 'BlockStatement') {
|
|
|
|
brkList.push({
|
|
|
|
start: node.body.start, end: node.body.end,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
});
|
|
|
|
brkList.sort((a, b) => {
|
|
|
|
if (a.start < b.start) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (a.start > b.start) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
});
|
|
|
|
for (let i = 0; i < brkList.length; i++) {
|
|
|
|
let check = null;
|
|
|
|
if (i > 0 && i < brkList.length - 1) {
|
|
|
|
check =
|
|
|
|
brkList[i].end >= brkList[i + 1].end ||
|
|
|
|
brkList[i].end <= brkList[i - 1].end;
|
|
|
|
if (i > 1) {
|
|
|
|
check = check || brkList[i].end <= brkList[i - 2].end;
|
|
|
|
}
|
|
|
|
} else if (i === 0) {
|
|
|
|
check = brkList[i].end >= brkList[i + 1].end;
|
|
|
|
} else if (i === brkList.length - 1) {
|
|
|
|
check = brkList[i].end <= brkList[i - 1].end;
|
|
|
|
}
|
|
|
|
brkList[i].nested = check;
|
|
|
|
const elem = Object.assign(brkList[i]);
|
|
|
|
elem.start += offset;
|
|
|
|
elem.end += offset;
|
|
|
|
contract = blockWrap(contract, elem);
|
|
|
|
offset += 2;
|
|
|
|
}
|
|
|
|
return contract;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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. The process is broken down to 3 stages:
|
|
|
|
* 1. Parse the contract to get the AST tree
|
|
|
|
* 2. Remove all the view, pure and constant modifiers
|
|
|
|
* 3. Bracket all related unbracketed singleton statements
|
|
|
|
* @param {String} contract solidity code
|
|
|
|
* @return {String} contract
|
|
|
|
*/
|
|
|
|
module.exports.run = function r(contract) {
|
|
|
|
try {
|
|
|
|
const ast = SolidityParser.parse(contract);
|
|
|
|
contract = removeViewPureConst(ast, contract);
|
|
|
|
contract = bracketUnbStatements(ast, contract);
|
|
|
|
} catch (err) {
|
|
|
|
contract = err;
|
|
|
|
}
|
|
|
|
return contract;
|
|
|
|
};
|