From 4b893c39a825abf9af6d8c4384ebf38c254feb17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Tue, 12 Feb 2019 11:02:36 +0100 Subject: [PATCH] Fix recursive functions --- .../tdz/function-call-recursive-after/exec.js | 9 +++++++++ .../tdz/function-call-recursive-after/input.js | 7 +++++++ .../tdz/function-call-recursive-after/output.js | 7 +++++++ .../tdz/function-call-recursive-before/exec.js | 9 +++++++++ .../tdz/function-call-recursive-before/input.js | 7 +++++++ .../tdz/function-call-recursive-before/output.js | 7 +++++++ .../tdz/function-call-recursive-reference/exec.js | 12 ++++++++++++ .../tdz/function-call-recursive-reference/input.js | 10 ++++++++++ .../tdz/function-call-recursive-reference/output.js | 12 ++++++++++++ packages/babel-traverse/src/path/introspection.js | 12 ++++++++++++ 10 files changed, 92 insertions(+) create mode 100644 packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-recursive-after/exec.js create mode 100644 packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-recursive-after/input.js create mode 100644 packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-recursive-after/output.js create mode 100644 packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-recursive-before/exec.js create mode 100644 packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-recursive-before/input.js create mode 100644 packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-recursive-before/output.js create mode 100644 packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-recursive-reference/exec.js create mode 100644 packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-recursive-reference/input.js create mode 100644 packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-recursive-reference/output.js diff --git a/packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-recursive-after/exec.js b/packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-recursive-after/exec.js new file mode 100644 index 000000000000..0e7185778c41 --- /dev/null +++ b/packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-recursive-after/exec.js @@ -0,0 +1,9 @@ +expect(() => { + function f(i) { + if (i) f(i - 1); + x; + } + + let x; + f(3); +}).not.toThrow(); diff --git a/packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-recursive-after/input.js b/packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-recursive-after/input.js new file mode 100644 index 000000000000..d50d6a69743e --- /dev/null +++ b/packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-recursive-after/input.js @@ -0,0 +1,7 @@ +function f(i) { + if (i) f(i - 1); + x; +} + +let x; +f(3); diff --git a/packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-recursive-after/output.js b/packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-recursive-after/output.js new file mode 100644 index 000000000000..c63e0af30e2d --- /dev/null +++ b/packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-recursive-after/output.js @@ -0,0 +1,7 @@ +function f(i) { + if (i) f(i - 1); + x; +} + +var x; +f(3); diff --git a/packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-recursive-before/exec.js b/packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-recursive-before/exec.js new file mode 100644 index 000000000000..eef606be9fe5 --- /dev/null +++ b/packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-recursive-before/exec.js @@ -0,0 +1,9 @@ +expect(() => { + function f(i) { + if (i) f(i - 1); + x; + } + + f(3); + let x; +}).toThrow(ReferenceError); diff --git a/packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-recursive-before/input.js b/packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-recursive-before/input.js new file mode 100644 index 000000000000..b07cb1e2235f --- /dev/null +++ b/packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-recursive-before/input.js @@ -0,0 +1,7 @@ +function f(i) { + if (i) f(i - 1); + x; +} + +f(3); +let x; diff --git a/packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-recursive-before/output.js b/packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-recursive-before/output.js new file mode 100644 index 000000000000..38da18f3e1ea --- /dev/null +++ b/packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-recursive-before/output.js @@ -0,0 +1,7 @@ +function f(i) { + if (i) f(i - 1); + babelHelpers.tdz("x"); +} + +f(3); +var x; diff --git a/packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-recursive-reference/exec.js b/packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-recursive-reference/exec.js new file mode 100644 index 000000000000..7dd31073305c --- /dev/null +++ b/packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-recursive-reference/exec.js @@ -0,0 +1,12 @@ +expect(() => { + function f(i) { + return () => { + x; + f(i - 1); + }; + } + + const g = f(1); + let x; + g(); +}).not.toThrow(); diff --git a/packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-recursive-reference/input.js b/packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-recursive-reference/input.js new file mode 100644 index 000000000000..07c586249f11 --- /dev/null +++ b/packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-recursive-reference/input.js @@ -0,0 +1,10 @@ +function f(i) { + return () => { + x; + f(i - 1); + }; +} + +const g = f(1); +let x; +g(); diff --git a/packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-recursive-reference/output.js b/packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-recursive-reference/output.js new file mode 100644 index 000000000000..0621da46002f --- /dev/null +++ b/packages/babel-plugin-transform-block-scoping/test/fixtures/tdz/function-call-recursive-reference/output.js @@ -0,0 +1,12 @@ +var x = babelHelpers.temporalUndefined; + +function f(i) { + return () => { + babelHelpers.temporalRef(x, "x"); + f(i - 1); + }; +} + +var g = f(1); +x = void 0; +g(); diff --git a/packages/babel-traverse/src/path/introspection.js b/packages/babel-traverse/src/path/introspection.js index 648fc442268c..ce7b46847bc6 100644 --- a/packages/babel-traverse/src/path/introspection.js +++ b/packages/babel-traverse/src/path/introspection.js @@ -361,6 +361,12 @@ export function _guessExecutionStatusRelativeTo( return keyPosition.target > keyPosition.this ? "before" : "after"; } +// Used to avoid infinite recursion in cases like +// function f() { if (false) f(); } +// f(); +// It also works with indirect recursion. +const executionOrderCheckedNodes = new WeakSet(); + export function _guessExecutionStatusRelativeToDifferentFunctions( target: NodePath, ): RelativeExecutionStatus { @@ -392,8 +398,14 @@ export function _guessExecutionStatusRelativeToDifferentFunctions( return "unknown"; } + // Prevent infinte loops in recursive functions + if (executionOrderCheckedNodes.has(path.node)) continue; + executionOrderCheckedNodes.add(path.node); + const status = this._guessExecutionStatusRelativeTo(path); + executionOrderCheckedNodes.delete(path.node); + if (allStatus && allStatus !== status) { return "unknown"; } else {