Skip to content

Commit 6319154

Browse files
authored
Fix infinite loops (codesandbox#401)
* Fix infinite loops * Change max iterations
1 parent 4607dad commit 6319154

File tree

2 files changed

+58
-0
lines changed

2 files changed

+58
-0
lines changed

packages/app/src/sandbox/eval/transpilers/babel/babel-worker.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import flatten from 'lodash/flatten';
44
import dynamicImportPlugin from './plugins/babel-plugin-dynamic-import-node';
55
import detective from './plugins/babel-plugin-detective';
6+
import infiniteLoops from './plugins/babel-plugin-transform-prevent-infinite-loops';
67

78
import { buildWorkerError } from '../utils/worker-error-handler';
89
import getDependencies from './get-require-statements';
@@ -27,6 +28,10 @@ declare var Babel: {
2728

2829
Babel.registerPlugin('dynamic-import-node', dynamicImportPlugin);
2930
Babel.registerPlugin('babel-plugin-detective', detective);
31+
Babel.registerPlugin(
32+
'babel-plugin-transform-prevent-infinite-loops',
33+
infiniteLoops
34+
);
3035

3136
self.addEventListener('message', async event => {
3237
const { code, path, config } = event.data;
@@ -51,6 +56,7 @@ self.addEventListener('message', async event => {
5156
...config.plugins,
5257
'dynamic-import-node',
5358
['babel-plugin-detective', { source: true, nodes: true }],
59+
'babel-plugin-transform-prevent-infinite-loops',
5460
];
5561

5662
const customConfig = {
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/**
2+
* Copyright (c) 2013-present, Facebook, Inc.
3+
* Copyright (c) 2017, Amjad Masad
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
// Based on https://repl.it/site/blog/infinite-loops.
10+
11+
const MAX_ITERATIONS = 5000;
12+
13+
module.exports = ({ types: t, template }) => {
14+
// We set a global so that we can later fail the test
15+
// even if the error ends up being caught by the code.
16+
const buildGuard = template(`
17+
if (ITERATOR++ > MAX_ITERATIONS) {
18+
global.infiniteLoopError = new RangeError(
19+
'Potential infinite loop: exceeded ' +
20+
MAX_ITERATIONS +
21+
' iterations.'
22+
);
23+
throw global.infiniteLoopError;
24+
}
25+
`);
26+
27+
return {
28+
visitor: {
29+
'WhileStatement|ForStatement|DoWhileStatement': (path, file) => {
30+
// An iterator that is incremented with each iteration
31+
const iterator = path.scope.parent.generateUidIdentifier('loopIt');
32+
const iteratorInit = t.numericLiteral(0);
33+
path.scope.parent.push({
34+
id: iterator,
35+
init: iteratorInit,
36+
});
37+
// If statement and throw error if it matches our criteria
38+
const guard = buildGuard({
39+
ITERATOR: iterator,
40+
MAX_ITERATIONS: t.numericLiteral(MAX_ITERATIONS),
41+
});
42+
// No block statment e.g. `while (1) 1;`
43+
if (!path.get('body').isBlockStatement()) {
44+
const statement = path.get('body').node;
45+
path.get('body').replaceWith(t.blockStatement([guard, statement]));
46+
} else {
47+
path.get('body').unshiftContainer('body', guard);
48+
}
49+
},
50+
},
51+
};
52+
};

0 commit comments

Comments
 (0)