Merge pull request #259 from h3ph4est7s/h3ph4est7s-patch-1

Honor excluded files & work towards fixing performance problem at preprocessor.
preprocessor-improvements
c-g-e-w-e-k-e- 6 years ago committed by GitHub
commit 5b71c44708
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      lib/app.js
  2. 169
      lib/preprocessor.js
  3. 1083
      test/sources/statements/unbracketed-sigleton-multi.sol

@ -388,6 +388,7 @@ class App {
*/ */
postProcessPure(env) { postProcessPure(env) {
shell.ls(`${env}/**/*.sol`).forEach(file => { shell.ls(`${env}/**/*.sol`).forEach(file => {
if (this.skipFiles.includes(file) || this.inSkippedFolder(file)) return;
const contractPath = this.platformNeutralPath(file); const contractPath = this.platformNeutralPath(file);
const contract = fs.readFileSync(contractPath).toString(); const contract = fs.readFileSync(contractPath).toString();
const contractProcessed = preprocessor.run(contract); const contractProcessed = preprocessor.run(contract);

@ -1,92 +1,125 @@
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`; * Splices enclosing brackets into `contract` around `expression`;
* @param {String} contract solidity source * @param {String} contract solidity source
* @param {Object} node AST node to bracket * @param {Object} expression AST node to bracket
* @return {String} contract * @return {String} contract
*/ */
function blockWrap(contract, expression) { function blockWrap(contract, expression) {
return contract.slice(0, expression.start) + '{' + contract.slice(expression.start, expression.end) + '}' + contract.slice(expression.end); 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;
}
/** /**
* 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) || []; let offset = 0;
return whitespace.join(''); 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 * 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;
}; };

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save