From babc9c90b46ae36eb670a2b487b23103ec3a2647 Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Tue, 7 Jul 2015 11:11:58 +0100 Subject: [PATCH] fix tail call recursion on functions with less arguments than parameters - fixes #1938 --- .../transformers/es6/tail-call.js | 25 +++++++++++------ .../transformation/transformers/other/flow.js | 1 + .../es6.tail-call/parameter-exceeds/actual.js | 15 ++++++++++ .../es6.tail-call/parameter-exceeds/exec.js | 9 ++++++ .../parameter-exceeds/expected.js | 28 +++++++++++++++++++ 5 files changed, 70 insertions(+), 8 deletions(-) create mode 100644 test/core/fixtures/transformation/es6.tail-call/parameter-exceeds/actual.js create mode 100644 test/core/fixtures/transformation/es6.tail-call/parameter-exceeds/exec.js create mode 100644 test/core/fixtures/transformation/es6.tail-call/parameter-exceeds/expected.js diff --git a/src/babel/transformation/transformers/es6/tail-call.js b/src/babel/transformation/transformers/es6/tail-call.js index 610b63f8e5..15738db793 100644 --- a/src/babel/transformation/transformers/es6/tail-call.js +++ b/src/babel/transformation/transformers/es6/tail-call.js @@ -337,10 +337,6 @@ class TailCallTransformer { args = t.arrayExpression(node.arguments); } - if (t.isArrayExpression(args) && args.elements.length > this.node.params.length) { - this.needsArguments = true; - } - var argumentsId = this.getArgumentsId(); var params = this.getParams(); @@ -354,17 +350,30 @@ class TailCallTransformer { if (t.isArrayExpression(args)) { var elems = args.elements; - for (let i = 0; i < elems.length && i < params.length; i++) { + + // pad out the args so all the function args are reset - https://github.com/babel/babel/issues/1938 + while (elems.length < params.length) { + elems.push(t.identifier("undefined")); + } + + for (let i = 0; i < elems.length; i++) { let param = params[i]; - let elem = elems[i] || (elems[i] = t.identifier("undefined")); - if (!param._isDefaultPlaceholder) { + let elem = elems[i]; + + if (param && !param._isDefaultPlaceholder) { elems[i] = t.assignmentExpression("=", param, elem); + } else { + // exceeds parameters but push it anyway to ensure correct execution } } if (!this.needsArguments) { for (let elem of (elems: Array)) { - body.push(t.expressionStatement(elem)); + // only push expressions that we really need, this will skip pure arguments that exceed the + // parameter length of the current function + if (!this.scope.isPure(elem)) { + body.push(t.expressionStatement(elem)); + } } } } else { diff --git a/src/babel/transformation/transformers/other/flow.js b/src/babel/transformation/transformers/other/flow.js index 84dda2a7d5..bb7184576f 100644 --- a/src/babel/transformation/transformers/other/flow.js +++ b/src/babel/transformation/transformers/other/flow.js @@ -12,6 +12,7 @@ export var visitor = { } } }, + Flow() { this.dangerouslyRemove(); }, diff --git a/test/core/fixtures/transformation/es6.tail-call/parameter-exceeds/actual.js b/test/core/fixtures/transformation/es6.tail-call/parameter-exceeds/actual.js new file mode 100644 index 0000000000..1887c13c91 --- /dev/null +++ b/test/core/fixtures/transformation/es6.tail-call/parameter-exceeds/actual.js @@ -0,0 +1,15 @@ +function foo(a, b) { + if (b) { + return foo(b); + } else { + return a; + } +} + +function foo(a, b) { + if (b) { + return foo("a", "b", "c"); + } else { + return a; + } +} diff --git a/test/core/fixtures/transformation/es6.tail-call/parameter-exceeds/exec.js b/test/core/fixtures/transformation/es6.tail-call/parameter-exceeds/exec.js new file mode 100644 index 0000000000..5053794f69 --- /dev/null +++ b/test/core/fixtures/transformation/es6.tail-call/parameter-exceeds/exec.js @@ -0,0 +1,9 @@ +function foo(a, b) { + if (b) { + return foo(b); + } else { + return a; + } +} + +assert.equal(foo("Michael", "Jackson"), "Jackson"); diff --git a/test/core/fixtures/transformation/es6.tail-call/parameter-exceeds/expected.js b/test/core/fixtures/transformation/es6.tail-call/parameter-exceeds/expected.js new file mode 100644 index 0000000000..c666b193c7 --- /dev/null +++ b/test/core/fixtures/transformation/es6.tail-call/parameter-exceeds/expected.js @@ -0,0 +1,28 @@ +"use strict"; + +function foo(a, b) { + if (b) { + return foo(b); + } else { + return a; + } +} + +function foo(_x, _x2) { + var _again = true; + + _function: while (_again) { + var a = _x, + b = _x2; + _again = false; + + if (b) { + _x = "a"; + _x2 = "b"; + _again = true; + continue _function; + } else { + return a; + } + } +}