From 71320c06a13f3c62019d44a86b5d357aa2dfe3c9 Mon Sep 17 00:00:00 2001 From: panos Date: Sat, 14 Jul 2018 03:01:34 +0300 Subject: [PATCH] fix JSDoc namings, fix spacing, 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/IbYzRM8Y --- lib/preprocessor.js | 161 ++++++++++++++++++++++++-------------------- 1 file changed, 89 insertions(+), 72 deletions(-) diff --git a/lib/preprocessor.js b/lib/preprocessor.js index c584748..1a64e80 100644 --- a/lib/preprocessor.js +++ b/lib/preprocessor.js @@ -1,92 +1,109 @@ 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 + * 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 blockWrap(contract, expression) { - return contract.slice(0, expression.start) + '{' + contract.slice(expression.start, expression.end) + '}' + contract.slice(expression.end); +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; } - /** - * 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 + * 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 getModifierWhitespace(contract, modifier){ - const source = contract.slice(modifier.start, modifier.end); - const whitespace = source.match(crRegex) || []; - return whitespace.join(''); +function bracketUnbStatements(ast, contract) { + const brkList = []; + 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; + } + 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. 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. - * + * 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 + * @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; - } + try { + const ast = SolidityParser.parse(contract); + contract = removeViewPureConst(ast, contract); + contract = bracketUnbStatements(ast, contract); + } catch (err) { + contract = err; } return contract; };