diff --git a/.eslintrc.json b/.eslintrc.json index fa8f161..2846ddf 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -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", diff --git a/src/index.html b/src/index.html index ac12762..d250647 100644 --- a/src/index.html +++ b/src/index.html @@ -222,7 +222,6 @@
  • Codemirror - Marijn Haverbeke
  • Emmet - Sergey Chikuyonok
  • Esprima - Ariya Hidayat
  • -
  • Escodegen - Yusuke Suzuki
  • Inlet - Ian Johnson
  • Web Maker! - whhat!
  • @@ -573,7 +572,6 @@ - diff --git a/src/script.js b/src/script.js index d6e8304..acf71fb 100644 --- a/src/script.js +++ b/src/script.js @@ -610,9 +610,9 @@ onboardDontShowInTabOptionBtn, TextareaAutoComplete, savedItemCountEl, indentati 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 +625,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(code); } - d.resolve(escodegen.generate(ast)); + d.resolve(code); } } else if (jsMode === JsModes.ES6) { if (!window.Babel) { @@ -650,19 +647,18 @@ onboardDontShowInTabOptionBtn, TextareaAutoComplete, savedItemCountEl, indentati // No JSX block // result = escodegen.generate(ast); if (shouldPreventInfiniteLoops !== false) { - utils.addInfiniteLoopProtection(ast); + code = utils.addInfiniteLoopProtection(code); } - d.resolve(Babel.transform(escodegen.generate(ast), { presets: ['latest', 'stage-2', 'react'] }).code); + // FIX ME, add jsx for loop protection above + d.resolve(Babel.transform(code, { 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); + code = utils.addInfiniteLoopProtection(code); } - d.resolve(escodegen.generate(ast)); + d.resolve(code); } } } else if (jsMode === JsModes.TS) { @@ -677,17 +673,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(ast); } + d.resolve(code); } catch (e) { showErrors('js', [ { lineNumber: e.lineNumber - 1, message: e.description } ]); } diff --git a/src/utils.js b/src/utils.js index 62c9af7..073417a 100644 --- a/src/utils.js +++ b/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 }, 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) {