Skip to content

Commit be40493

Browse files
committed
preventing infinite loops
1 parent 53ae28f commit be40493

File tree

5 files changed

+430
-375
lines changed

5 files changed

+430
-375
lines changed

packages/jsrepl/src/hooks/useCodeEditorRepl.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,6 @@ export default function useCodeEditorRepl(
290290

291291
function updateToken() {
292292
replDataRef.current = {
293-
token: (replDataRef.current.token + 1) % Number.MAX_VALUE,
293+
token: (replDataRef.current.token + 1) % Number.MAX_SAFE_INTEGER,
294294
}
295295
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Based on https://github.com/facebook/react/blob/ff595de29af107255fd957ca809d3074c16bcf12/scripts/babel/transform-prevent-infinite-loops.js
2+
3+
/**
4+
* Copyright (c) Meta Platforms, Inc. and affiliates.
5+
* Copyright (c) 2017, Amjad Masad
6+
*
7+
* This source code is licensed under the MIT license found in the
8+
* LICENSE file in the root directory of this source tree.
9+
*/
10+
import type { NodePath, PluginObj, template as templateBuilder, types } from '@babel/core'
11+
12+
// Based on https://repl.it/site/blog/infinite-loops.
13+
14+
const MAX_ITERATIONS = 6000
15+
16+
export function preventInfiniteLoopsPlugin({
17+
types: t,
18+
template,
19+
}: {
20+
types: typeof types
21+
template: typeof templateBuilder
22+
}): PluginObj {
23+
const buildGuard = template.statement(`
24+
if (%%iterator%%++ > %%maxIterations%%) {
25+
throw new RangeError(
26+
'Potential infinite loop: exceeded ' + %%maxIterations%% + ' iterations.'
27+
);
28+
}
29+
`)
30+
31+
return {
32+
visitor: {
33+
'WhileStatement|ForStatement|DoWhileStatement'(_path) {
34+
const path = _path as NodePath<
35+
types.WhileStatement | types.ForStatement | types.DoWhileStatement
36+
>
37+
38+
// An iterator that is incremented with each iteration
39+
const iterator = path.scope.parent.generateUidIdentifier('loopIt')
40+
const iteratorInit = t.numericLiteral(0)
41+
path.scope.parent.push({
42+
id: iterator,
43+
init: iteratorInit,
44+
})
45+
// If statement and throw error if it matches our criteria
46+
const guard = buildGuard({
47+
iterator,
48+
maxIterations: t.numericLiteral(MAX_ITERATIONS),
49+
})
50+
const bodyPath = path.get('body')
51+
// No block statement e.g. `while (1) 1;`
52+
if (!bodyPath.isBlockStatement()) {
53+
const statement = bodyPath.node
54+
bodyPath.replaceWith(t.blockStatement([guard, statement]))
55+
} else {
56+
bodyPath.unshiftContainer('body', guard)
57+
}
58+
},
59+
},
60+
}
61+
}

0 commit comments

Comments
 (0)