separate preprocessing functionality in stages, change method of view, pure and constant removal, start the algorithm to detect unbracketed singleton statements nesting and calculate offset properly, remove unnecessary functions, modifier removal result: https://www.diffchecker.com/IbYzRM8Ypull/259/head
parent
b39a31a76d
commit
71320c06a1
@ -1,92 +1,109 @@ |
|||||||
const SolExplore = require('sol-explore'); |
const SolExplore = require('sol-explore'); |
||||||
const SolidityParser = require('solidity-parser-sc'); |
const SolidityParser = require('solidity-parser-sc'); |
||||||
|
|
||||||
const crRegex = /[\r\n ]+$/g; |
|
||||||
/** |
/** |
||||||
* Splices enclosing brackets into `contract` around `expression`; |
* Parses the AST tree to remove pure, constant and view modifiers |
||||||
* @param {String} contract solidity source |
* @param {Object} ast AST tree |
||||||
* @param {Object} node AST node to bracket |
* @param {String} contract solidity code |
||||||
* @return {String} contract |
* @return {String} contract |
||||||
*/ |
*/ |
||||||
function blockWrap(contract, expression) { |
function removeViewPureConst(ast, contract) { |
||||||
return contract.slice(0, expression.start) + '{' + contract.slice(expression.start, expression.end) + '}' + contract.slice(expression.end); |
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; |
||||||
} |
} |
||||||
|
|
||||||
|
|
||||||
/** |
/** |
||||||
* Captures carriage returns at modifiers we'll remove. These need to be re-injected into the |
* Parses the AST tree to locate unbracketed singleton statements attached to if, else, for and while statements |
||||||
* source to keep line report alignments accurate. |
* and brackets them |
||||||
* @param {String} contract solidity source |
* @param {Object} ast AST tree product of SolidityParser |
||||||
* @param {Object} modifier AST node |
* @param {String} contract solidity code |
||||||
* @return {String} whitespace around the modifier |
* @return {String} contract |
||||||
*/ |
*/ |
||||||
function getModifierWhitespace(contract, modifier){ |
function bracketUnbStatements(ast, contract) { |
||||||
const source = contract.slice(modifier.start, modifier.end); |
const brkList = []; |
||||||
const whitespace = source.match(crRegex) || []; |
SolExplore.traverse(ast, { |
||||||
return whitespace.join(''); |
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; |
||||||
|
} |
||||||
|
return contract; |
||||||
} |
} |
||||||
|
|
||||||
/** |
/** |
||||||
* Locates unbracketed singleton statements attached to if, else, for and while statements |
* 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 |
* 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 |
* them pre-bracketed simplifies the process. The process is broken down to 3 stages: |
||||||
* is passed back to the parser and re-walked because all the starts and ends get shifted. |
* 1. Parse the contract to get the AST tree |
||||||
* |
* 2. Remove all the view, pure and constant modifiers |
||||||
* Also removes pure and view modifiers. |
* 3. Bracket all related unbracketed singleton statements |
||||||
* |
|
||||||
* @param {String} contract solidity code |
* @param {String} contract solidity code |
||||||
* @return {String} contract |
* @return {String} contract |
||||||
*/ |
*/ |
||||||
module.exports.run = function r(contract) { |
module.exports.run = function r(contract) { |
||||||
let keepRunning = true; |
try { |
||||||
|
const ast = SolidityParser.parse(contract); |
||||||
while (keepRunning) { |
contract = removeViewPureConst(ast, contract); |
||||||
try { |
contract = bracketUnbStatements(ast, contract); |
||||||
const ast = SolidityParser.parse(contract); |
} catch (err) { |
||||||
keepRunning = false; |
contract = err; |
||||||
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; |
return contract; |
||||||
}; |
}; |
||||||
|
Loading…
Reference in new issue