mirror of
https://github.com/chinchang/web-maker.git
synced 2025-02-24 07:03:13 +01:00
Merge pull request #98 from chinchang/infinite-loop-improvement
Use esprima's syntax delegate to remove use of escodegen. fixes #93 f…
This commit is contained in:
commit
9470890681
@ -133,12 +133,7 @@
|
||||
}
|
||||
],
|
||||
"no-path-concat": "error",
|
||||
"no-plusplus": [
|
||||
"error",
|
||||
{
|
||||
"allowForLoopAfterthoughts": true
|
||||
}
|
||||
],
|
||||
"no-plusplus": "off",
|
||||
"no-process-env": "error",
|
||||
"no-process-exit": "error",
|
||||
"no-proto": "error",
|
||||
|
@ -222,7 +222,6 @@
|
||||
<li><a target="_blank" href="https://codemirror.net/">Codemirror</a> - Marijn Haverbeke</li>
|
||||
<li><a target="_blank" href="https://emmet.io/">Emmet</a> - Sergey Chikuyonok</li>
|
||||
<li><a target="_blank" href="http://esprima.org/">Esprima</a> - Ariya Hidayat</li>
|
||||
<li><a target="_blank" href="https://github.com/estools/escodegen">Escodegen</a> - Yusuke Suzuki</li>
|
||||
<li><a target="_blank" href="https://github.com/enjalot/Inlet">Inlet</a> - Ian Johnson</li>
|
||||
<li><a target="_blank" href="https://kushagragour.in/lab/web-maker">Web Maker!</a> - whhat!</li>
|
||||
</ul>
|
||||
@ -573,7 +572,6 @@
|
||||
<script src="lib/split.js"></script>
|
||||
<script src="lib/inlet.min.js"></script>
|
||||
<script src="lib/esprima.js"></script>
|
||||
<script src="lib/escodegen.browser.min.js"></script>
|
||||
|
||||
<script src="utils.js"></script>
|
||||
<script src="analytics.js"></script>
|
||||
|
@ -599,20 +599,19 @@ onboardDontShowInTabOptionBtn, TextareaAutoComplete, savedItemCountEl, indentati
|
||||
d.resolve('');
|
||||
return d.promise;
|
||||
}
|
||||
var ast;
|
||||
|
||||
if (jsMode === JsModes.JS) {
|
||||
try {
|
||||
ast = esprima.parse(code, {
|
||||
esprima.parse(code, {
|
||||
tolerant: true
|
||||
});
|
||||
} catch (e) {
|
||||
showErrors('js', [ { lineNumber: e.lineNumber - 1, message: e.description } ]);
|
||||
} finally {
|
||||
if (shouldPreventInfiniteLoops !== false) {
|
||||
utils.addInfiniteLoopProtection(ast);
|
||||
code = utils.addInfiniteLoopProtection(code);
|
||||
}
|
||||
d.resolve(escodegen.generate(ast));
|
||||
d.resolve(code);
|
||||
}
|
||||
} else if (jsMode === JsModes.COFFEESCRIPT) {
|
||||
var coffeeCode;
|
||||
@ -625,13 +624,10 @@ onboardDontShowInTabOptionBtn, TextareaAutoComplete, savedItemCountEl, indentati
|
||||
} catch (e) {
|
||||
showErrors('js', [ { lineNumber: e.location.first_line, message: e.message } ]);
|
||||
} finally {
|
||||
ast = esprima.parse(coffeeCode, {
|
||||
tolerant: true
|
||||
});
|
||||
if (shouldPreventInfiniteLoops !== false) {
|
||||
utils.addInfiniteLoopProtection(ast);
|
||||
code = utils.addInfiniteLoopProtection(coffeeCode);
|
||||
}
|
||||
d.resolve(escodegen.generate(ast));
|
||||
d.resolve(code);
|
||||
}
|
||||
} else if (jsMode === JsModes.ES6) {
|
||||
if (!window.Babel) {
|
||||
@ -639,31 +635,18 @@ onboardDontShowInTabOptionBtn, TextareaAutoComplete, savedItemCountEl, indentati
|
||||
return d.promise;
|
||||
}
|
||||
try {
|
||||
ast = esprima.parse(code, {
|
||||
esprima.parse(code, {
|
||||
tolerant: true,
|
||||
jsx: true
|
||||
});
|
||||
} catch (e) {
|
||||
showErrors('js', [ { lineNumber: e.lineNumber - 1, message: e.description } ]);
|
||||
} finally {
|
||||
try {
|
||||
// No JSX block
|
||||
// result = escodegen.generate(ast);
|
||||
if (shouldPreventInfiniteLoops !== false) {
|
||||
utils.addInfiniteLoopProtection(ast);
|
||||
}
|
||||
d.resolve(Babel.transform(escodegen.generate(ast), { presets: ['latest', 'stage-2', 'react'] }).code);
|
||||
} catch (e) {
|
||||
// If we failed, means probably the AST contains JSX which cannot be parsed by escodegen.
|
||||
code = Babel.transform(code, { presets: ['latest', 'stage-2', 'react'] }).code;
|
||||
ast = esprima.parse(code, {
|
||||
tolerant: true
|
||||
});
|
||||
if (shouldPreventInfiniteLoops !== false) {
|
||||
utils.addInfiniteLoopProtection(ast);
|
||||
}
|
||||
d.resolve(escodegen.generate(ast));
|
||||
code = Babel.transform(code, { presets: ['latest', 'stage-2', 'react'] }).code;
|
||||
if (shouldPreventInfiniteLoops !== false) {
|
||||
code = utils.addInfiniteLoopProtection(code);
|
||||
}
|
||||
d.resolve(code);
|
||||
}
|
||||
} else if (jsMode === JsModes.TS) {
|
||||
try {
|
||||
@ -677,17 +660,10 @@ onboardDontShowInTabOptionBtn, TextareaAutoComplete, savedItemCountEl, indentati
|
||||
/* eslint-disable no-throw-literal */
|
||||
throw ({ description: code.diagnostics[0].messageText, lineNumber: ts.getLineOfLocalPosition(code.diagnostics[0].file,code.diagnostics[0].start) });
|
||||
}
|
||||
try {
|
||||
ast = esprima.parse(code.outputText, {
|
||||
tolerant: true,
|
||||
jsx: true
|
||||
});
|
||||
} finally {
|
||||
if (shouldPreventInfiniteLoops !== false) {
|
||||
utils.addInfiniteLoopProtection(ast);
|
||||
}
|
||||
d.resolve(escodegen.generate(ast));
|
||||
if (shouldPreventInfiniteLoops !== false) {
|
||||
code = utils.addInfiniteLoopProtection(code.outputText);
|
||||
}
|
||||
d.resolve(code);
|
||||
} catch (e) {
|
||||
showErrors('js', [ { lineNumber: e.lineNumber - 1, message: e.description } ]);
|
||||
}
|
||||
|
85
src/utils.js
85
src/utils.js
@ -42,52 +42,55 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Generate 2 ASTs for the code to be inserted in loops for infinite run protection.
|
||||
// The `myVar` variable names would be changed later for every insertion.
|
||||
function getLoopProtectorBlocks() {
|
||||
var ast1 = esprima.parse('var myvar = Date.now();');
|
||||
var ast2 = esprima.parse('while(a){if (Date.now() - a787897 > 1000) { window.top.previewException(new Error("Infinite loop")); break;}}');
|
||||
return {
|
||||
before: ast1.body[0],
|
||||
inside: ast2.body[0].body.body[0]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add timed limit on the loops found in the passed AST body
|
||||
* @param {ASTBody} Body of an AST generated by esprima or any ES compliant AST
|
||||
* Adds timed limit on the loops found in the passed code.
|
||||
* Contributed by Ariya Hidayat!
|
||||
* @param code {string} Code to be protected from infinite loops.
|
||||
*/
|
||||
function addInfiniteLoopProtection(astBody) {
|
||||
if (!astBody) { return; }
|
||||
if (!Array.isArray(astBody)) {
|
||||
addInfiniteLoopProtection(astBody.body);
|
||||
return;
|
||||
}
|
||||
var el, randomVariableName, insertionBLocks;
|
||||
function addInfiniteLoopProtection(code) {
|
||||
var loopId = 1;
|
||||
var patches = [];
|
||||
var varPrefix = '_wmloopvar';
|
||||
var varStr = 'var %d = Date.now();\n'
|
||||
var checkStr = '\nif (Date.now() - %d > 1000) { window.top.previewException(new Error("Infinite loop")); break;}\n'
|
||||
|
||||
for (var i = astBody.length; i--;) {
|
||||
el = astBody[i];
|
||||
if (el && el.type === 'ForStatement' || el.type === 'WhileStatement' || el.type === 'DoWhileStatement') {
|
||||
randomVariableName = '_' + generateRandomId(3);
|
||||
insertionBLocks = getLoopProtectorBlocks();
|
||||
insertionBLocks.before.declarations[0].id.name = insertionBLocks.inside.test.left.right.name = randomVariableName;
|
||||
// Insert time variable assignment
|
||||
astBody.splice(i, 0, insertionBLocks.before);
|
||||
// If the loop's body is a single statement, then convert it into a block statement
|
||||
// so that we can insert our conditional break inside it.
|
||||
if (!Array.isArray(el.body)) {
|
||||
el.body = {
|
||||
body: [ el.body ],
|
||||
type: 'BlockStatement'
|
||||
};
|
||||
esprima.parse(code, { tolerant: true, range: true, jsx: true }, function (node) {
|
||||
switch (node.type) {
|
||||
case 'DoWhileStatement':
|
||||
case 'ForStatement':
|
||||
case 'ForInStatement':
|
||||
case 'ForOfStatement':
|
||||
case 'WhileStatement':
|
||||
var start = 1 + node.body.range[0];
|
||||
var end = node.body.range[1];
|
||||
var prolog = checkStr.replace('%d', varPrefix + loopId);
|
||||
var epilog = '';
|
||||
|
||||
if (node.body.type !== 'BlockStatement') {
|
||||
// `while(1) doThat()` becomes `while(1) {doThat()}`
|
||||
prolog = '{' + prolog;
|
||||
epilog = '}';
|
||||
--start;
|
||||
}
|
||||
// Insert IfStatement
|
||||
el.body.body.unshift(insertionBLocks.inside);
|
||||
|
||||
patches.push({ pos: start, str: prolog });
|
||||
patches.push({ pos: end, str: epilog });
|
||||
patches.push({ pos: node.range[0], str: varStr.replace('%d', varPrefix + loopId) });
|
||||
++loopId;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (el.body) {
|
||||
addInfiniteLoopProtection(el.body);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/* eslint-disable no-param-reassign */
|
||||
patches.sort(function (a, b) { return b.pos - a.pos }).forEach(function (patch) {
|
||||
code = code.slice(0, patch.pos) + patch.str + code.slice(patch.pos);
|
||||
});
|
||||
|
||||
/* eslint-disable no-param-reassign */
|
||||
return code;
|
||||
}
|
||||
|
||||
function getHumanDate(timestamp) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user