From b83e0ec7b0cbd752f1cf6978173feda4740f3b21 Mon Sep 17 00:00:00 2001 From: Mauro Bringolf Date: Wed, 12 Jul 2017 23:36:44 +0200 Subject: [PATCH] 2nd try: Add loose option for es2015-parameters transformation (#5943) * Import changes to parameters package from previous branch * Refactor plugin option access via state --- .../README.md | 20 ++++++++ .../src/default.js | 50 ++++++++++++++++++- .../parameters/default-earlier-params/exec.js | 3 ++ .../complex-assignment/actual.js | 1 + .../complex-assignment/expected.js | 4 ++ .../default-array-destructuring/actual.js | 1 + .../default-array-destructuring/exec.js | 4 ++ .../default-array-destructuring/expected.js | 7 +++ .../default-earlier-params/exec.js | 3 ++ .../default-iife-1128/exec.js | 9 ++++ .../default-iife-4253/exec.js | 9 ++++ .../default-iife-self/exec.js | 7 +++ .../default-multiple/actual.js | 7 +++ .../default-multiple/expected.js | 19 +++++++ .../default-object-destructuring/exec.js | 19 +++++++ .../use-loose-option/default-rest/exec.js | 15 ++++++ .../use-loose-option/default-single/actual.js | 3 ++ .../default-single/expected.js | 7 +++ .../fixtures/use-loose-option/options.json | 3 ++ .../overwrite-undefined/exec.js | 5 ++ 20 files changed, 195 insertions(+), 1 deletion(-) create mode 100644 packages/babel-plugin-transform-es2015-parameters/test/fixtures/parameters/default-earlier-params/exec.js create mode 100644 packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/complex-assignment/actual.js create mode 100644 packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/complex-assignment/expected.js create mode 100644 packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-array-destructuring/actual.js create mode 100644 packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-array-destructuring/exec.js create mode 100644 packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-array-destructuring/expected.js create mode 100644 packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-earlier-params/exec.js create mode 100644 packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-iife-1128/exec.js create mode 100644 packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-iife-4253/exec.js create mode 100644 packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-iife-self/exec.js create mode 100644 packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-multiple/actual.js create mode 100644 packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-multiple/expected.js create mode 100644 packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-object-destructuring/exec.js create mode 100644 packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-rest/exec.js create mode 100644 packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-single/actual.js create mode 100644 packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-single/expected.js create mode 100644 packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/options.json create mode 100644 packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/overwrite-undefined/exec.js diff --git a/packages/babel-plugin-transform-es2015-parameters/README.md b/packages/babel-plugin-transform-es2015-parameters/README.md index 1587fe5c5b..1976fb4085 100644 --- a/packages/babel-plugin-transform-es2015-parameters/README.md +++ b/packages/babel-plugin-transform-es2015-parameters/README.md @@ -72,3 +72,23 @@ require("babel-core").transform("code", { plugins: ["transform-es2015-parameters"] }); ``` + +## Options + +### `loose` + +`boolean`, defaults to `false`. + +In loose mode, parameters with default values will be counted into the arity of the function. This is not spec behavior where these parameters do not add to function arity. + +The `loose` implementation is a more performant solution as JavaScript engines will fully optimize a function that doesn't reference `arguments`. Please do your own benchmarking and determine if this option is the right fit for your application. + +```javascript +// Spec behavior +function bar1 (arg1 = 1) {} +bar1.length // 0 + +// Loose mode +function bar1 (arg1 = 1) {} +bar1.length // 1 +``` diff --git a/packages/babel-plugin-transform-es2015-parameters/src/default.js b/packages/babel-plugin-transform-es2015-parameters/src/default.js index 49d6999bb9..f3b1875e76 100644 --- a/packages/babel-plugin-transform-es2015-parameters/src/default.js +++ b/packages/babel-plugin-transform-es2015-parameters/src/default.js @@ -11,6 +11,16 @@ const buildDefaultParam = template(` DEFAULT_VALUE; `); +const buildLooseDefaultParam = template(` + if (ASSIGMENT_IDENTIFIER === UNDEFINED) { + ASSIGMENT_IDENTIFIER = DEFAULT_VALUE; + } +`); + +const buildLooseDestructuredDefaultParam = template(` + let ASSIGMENT_IDENTIFIER = PARAMETER_NAME === UNDEFINED ? DEFAULT_VALUE : PARAMETER_NAME ; +`); + const buildCutOff = template(` let $0 = $1[$2]; `); @@ -51,6 +61,45 @@ export const visitor = { // ensure it's a block, useful for arrow functions path.ensureBlock(); + const params = path.get("params"); + + if (this.opts.loose) { + const body = []; + for (let i = 0; i < params.length; ++i) { + const param = params[i]; + if (param.isAssignmentPattern()) { + const left = param.get("left"); + const right = param.get("right"); + + const undefinedNode = scope.buildUndefinedNode(); + + if (left.isIdentifier()) { + body.push( + buildLooseDefaultParam({ + ASSIGMENT_IDENTIFIER: left.node, + DEFAULT_VALUE: right.node, + UNDEFINED: undefinedNode, + }), + ); + param.replaceWith(left.node); + } else if (left.isObjectPattern() || left.isArrayPattern()) { + const paramName = scope.generateUidIdentifier(); + body.push( + buildLooseDestructuredDefaultParam({ + ASSIGMENT_IDENTIFIER: left.node, + DEFAULT_VALUE: right.node, + PARAMETER_NAME: paramName, + UNDEFINED: undefinedNode, + }), + ); + param.replaceWith(paramName); + } + } + } + path.get("body").unshiftContainer("body", body); + return; + } + const state = { iife: false, scope: scope, @@ -77,7 +126,6 @@ export const visitor = { const lastNonDefaultParam = getFunctionArity(node); // - const params = path.get("params"); for (let i = 0; i < params.length; i++) { const param = params[i]; diff --git a/packages/babel-plugin-transform-es2015-parameters/test/fixtures/parameters/default-earlier-params/exec.js b/packages/babel-plugin-transform-es2015-parameters/test/fixtures/parameters/default-earlier-params/exec.js new file mode 100644 index 0000000000..75ce3a4f34 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-parameters/test/fixtures/parameters/default-earlier-params/exec.js @@ -0,0 +1,3 @@ +function f(a, b = a, c = b) { return c; } + +assert.equal(3, f(3)); diff --git a/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/complex-assignment/actual.js b/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/complex-assignment/actual.js new file mode 100644 index 0000000000..3613d3cae0 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/complex-assignment/actual.js @@ -0,0 +1 @@ +function test({a: b} = {}) {} diff --git a/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/complex-assignment/expected.js b/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/complex-assignment/expected.js new file mode 100644 index 0000000000..86f1aa634d --- /dev/null +++ b/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/complex-assignment/expected.js @@ -0,0 +1,4 @@ +function test(_temp) { + var _ref = _temp === void 0 ? {} : _temp, + b = _ref.a; +} diff --git a/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-array-destructuring/actual.js b/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-array-destructuring/actual.js new file mode 100644 index 0000000000..3d40455c49 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-array-destructuring/actual.js @@ -0,0 +1 @@ +function t([,,a] = [1,2,3]) { return a } diff --git a/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-array-destructuring/exec.js b/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-array-destructuring/exec.js new file mode 100644 index 0000000000..a500854065 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-array-destructuring/exec.js @@ -0,0 +1,4 @@ +function t([,,a] = [1,2,3]) { return a } + +assert.equal(t(), 3); +assert.equal(t([4,5,6]), 6); diff --git a/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-array-destructuring/expected.js b/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-array-destructuring/expected.js new file mode 100644 index 0000000000..dfe8a84390 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-array-destructuring/expected.js @@ -0,0 +1,7 @@ +function t(_temp) { + var _ref = _temp === void 0 ? [1, 2, 3] : _temp, + _ref2 = babelHelpers.slicedToArray(_ref, 3), + a = _ref2[2]; + + return a; +} diff --git a/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-earlier-params/exec.js b/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-earlier-params/exec.js new file mode 100644 index 0000000000..75ce3a4f34 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-earlier-params/exec.js @@ -0,0 +1,3 @@ +function f(a, b = a, c = b) { return c; } + +assert.equal(3, f(3)); diff --git a/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-iife-1128/exec.js b/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-iife-1128/exec.js new file mode 100644 index 0000000000..f9ec9718e4 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-iife-1128/exec.js @@ -0,0 +1,9 @@ +const bar = true; + +function foo(a = bar, ...b) { + const bar = false; + assert.equal(b[0], 2); + assert.equal(b[1], 3); +} + +foo(1, 2, 3); diff --git a/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-iife-4253/exec.js b/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-iife-4253/exec.js new file mode 100644 index 0000000000..8e0a435f11 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-iife-4253/exec.js @@ -0,0 +1,9 @@ +class Ref { + static nextId = 0 + constructor(id = ++Ref.nextId, n = id) { + this.id = n + } +} + +assert.equal(1, new Ref().id) +assert.equal(2, new Ref().id) diff --git a/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-iife-self/exec.js b/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-iife-self/exec.js new file mode 100644 index 0000000000..f1ad98139d --- /dev/null +++ b/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-iife-self/exec.js @@ -0,0 +1,7 @@ +class Ref { + constructor(ref = Ref) { + this.ref = ref + } +} + +assert.equal(Ref, new Ref().ref) diff --git a/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-multiple/actual.js b/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-multiple/actual.js new file mode 100644 index 0000000000..c5c20ad411 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-multiple/actual.js @@ -0,0 +1,7 @@ +var t = function (e = "foo", f = 5) { + return e + " bar " + f; +}; + +var a = function (e, f = 5) { + return e + " bar " + f; +}; diff --git a/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-multiple/expected.js b/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-multiple/expected.js new file mode 100644 index 0000000000..1773289d61 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-multiple/expected.js @@ -0,0 +1,19 @@ +var t = function (e, f) { + if (e === void 0) { + e = "foo"; + } + + if (f === void 0) { + f = 5; + } + + return e + " bar " + f; +}; + +var a = function (e, f) { + if (f === void 0) { + f = 5; + } + + return e + " bar " + f; +}; diff --git a/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-object-destructuring/exec.js b/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-object-destructuring/exec.js new file mode 100644 index 0000000000..3148c98145 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-object-destructuring/exec.js @@ -0,0 +1,19 @@ +function required(msg) { + throw new Error(msg); +} + +function sum( + { arr = required('arr is required') } = { arr: arr = [] }, + length = arr.length +) { + let i = 0; + let acc = 0; + for (let item of arr) { + if (i >= length) return acc; + acc += item; + i++; + } + return acc; +} + +assert.equal(sum({arr:[1,2]}), 3); diff --git a/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-rest/exec.js b/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-rest/exec.js new file mode 100644 index 0000000000..1f3eeb12c8 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-rest/exec.js @@ -0,0 +1,15 @@ +const a = 1; +function rest(b = a, ...a) { + assert.equal(b, 1); +} +rest(undefined, 2) + +function rest2(b = a, ...a) { + assert.equal(a[0], 2); +} +rest2(undefined, 2) + +function rest3(b = a, ...a) { + assert.equal(a.length, 1); +} +rest3(undefined, 2) diff --git a/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-single/actual.js b/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-single/actual.js new file mode 100644 index 0000000000..03030ea9af --- /dev/null +++ b/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-single/actual.js @@ -0,0 +1,3 @@ +var t = function (f = "foo") { + return f + " bar"; +}; diff --git a/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-single/expected.js b/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-single/expected.js new file mode 100644 index 0000000000..aaab07b4c0 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/default-single/expected.js @@ -0,0 +1,7 @@ +var t = function (f) { + if (f === void 0) { + f = "foo"; + } + + return f + " bar"; +}; diff --git a/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/options.json b/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/options.json new file mode 100644 index 0000000000..d7c9c408a0 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/options.json @@ -0,0 +1,3 @@ +{ + "plugins": ["transform-class-properties", "external-helpers", "syntax-flow", ["transform-es2015-parameters", { "loose": true } ], "transform-es2015-block-scoping", "transform-es2015-spread", "transform-es2015-classes", "transform-es2015-destructuring", "transform-es2015-arrow-functions", "syntax-async-functions", "transform-es2015-for-of"] +} diff --git a/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/overwrite-undefined/exec.js b/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/overwrite-undefined/exec.js new file mode 100644 index 0000000000..779e389e8d --- /dev/null +++ b/packages/babel-plugin-transform-es2015-parameters/test/fixtures/use-loose-option/overwrite-undefined/exec.js @@ -0,0 +1,5 @@ +function t(undefined = 17, a = 3) { + return a; +} + +assert.equal(t(), 3);