|
|
|
const SolExplore = require('sol-explore');
|
|
|
|
const SolidityParser = require('solidity-parser-antlr');
|
|
|
|
|
|
|
|
const crRegex = /[\r\n ]+$/g;
|
|
|
|
const OPEN = '{';
|
|
|
|
const CLOSE = '}';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Splices enclosing brackets into `contract` around `expression`;
|
|
|
|
* @param {String} contract solidity source
|
|
|
|
* @param {Object} node AST node to bracket
|
|
|
|
* @return {String} contract
|
|
|
|
*/
|
|
|
|
function insertBrace(contract, item, offset) {
|
|
|
|
return contract.slice(0,item.pos + offset) + item.type + contract.slice(item.pos + offset)
|
|
|
|
}
|
|
|
|
|
|
|
|
/** 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) {
|
|
|
|
|
|
|
|
try {
|
|
|
|
const ast = SolidityParser.parse(contract, { range: true });
|
|
|
|
insertions = [];
|
|
|
|
viewPureToRemove = [];
|
|
|
|
SolidityParser.visit(ast, {
|
|
|
|
IfStatement: function(node) {
|
|
|
|
if (node.trueBody.type !== 'Block') {
|
|
|
|
insertions.push({type: OPEN, pos: node.trueBody.range[0]});
|
|
|
|
insertions.push({type: CLOSE, pos: node.trueBody.range[1] + 1});
|
|
|
|
} else if ( node.falseBody && node.falseBody.type !== 'Block' ) {
|
|
|
|
insertions.push({type: OPEN, pos: node.falseBody.range[0]});
|
|
|
|
insertions.push({type: CLOSE, pos: node.falseBody.range[1] + 1});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
ForStatement: function(node){
|
|
|
|
if (node.body.type !== 'Block'){
|
|
|
|
insertions.push({type: OPEN, pos: node.body.range[0]});
|
|
|
|
insertions.push({type: CLOSE, pos: node.body.range[1] + 1});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
WhileStatement: function(node){
|
|
|
|
if (node.body.type !== 'Block'){
|
|
|
|
insertions.push({type: OPEN, pos: node.body.range[0]});
|
|
|
|
insertions.push({type: CLOSE, pos: node.body.range[1] + 1});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
FunctionDefinition: function(node){
|
|
|
|
if (node.stateMutability === 'view' || node.stateMutability === 'pure'){
|
|
|
|
viewPureToRemove.push(node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
// Firstly, remove pures and views. Note that we replace 'pure' and 'view' with spaces, so
|
|
|
|
// character counts remain the same, so we can do this in any order
|
|
|
|
viewPureToRemove.forEach(node => contract = removePureView(contract, node));
|
|
|
|
// Sort the insertion points.
|
|
|
|
insertions.sort((a,b) => a.pos - b.pos);
|
|
|
|
insertions.forEach((item, idx) => contract = insertBrace(contract, item, idx));
|
|
|
|
|
|
|
|
} catch (err) {
|
|
|
|
contract = err;
|
|
|
|
keepRunning = false;
|
|
|
|
}
|
|
|
|
return contract;
|
|
|
|
};
|