From 4b85b05839017ab2abc03ba2d1e875a63f002890 Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Fri, 5 Jun 2015 14:08:18 +0100 Subject: [PATCH] use actual parameter reference for non-last default parameters - fixes #1690 --- .../helpers/get-function-arity.js | 5 +- .../templates/default-parameter-assign.js | 1 + .../transformers/es6/parameters.default.js | 60 +++++++++++++------ .../before-last/actual.js | 3 + .../before-last/expected.js | 5 ++ 5 files changed, 54 insertions(+), 20 deletions(-) create mode 100644 src/babel/transformation/templates/default-parameter-assign.js create mode 100644 test/core/fixtures/transformation/es6.parameters.default/before-last/actual.js create mode 100644 test/core/fixtures/transformation/es6.parameters.default/before-last/expected.js diff --git a/src/babel/transformation/helpers/get-function-arity.js b/src/babel/transformation/helpers/get-function-arity.js index 98a4e42839..e59d86aad4 100644 --- a/src/babel/transformation/helpers/get-function-arity.js +++ b/src/babel/transformation/helpers/get-function-arity.js @@ -3,7 +3,10 @@ import * as t from "../../types"; export default function (node) { var lastNonDefault = 0; for (var i = 0; i < node.params.length; i++) { - if (!t.isAssignmentPattern(node.params[i])) lastNonDefault = i + 1; + var param = node.params[i]; + if (!t.isAssignmentPattern(param) && !t.isRestElement(param)) { + lastNonDefault = i + 1; + } } return lastNonDefault; } diff --git a/src/babel/transformation/templates/default-parameter-assign.js b/src/babel/transformation/templates/default-parameter-assign.js new file mode 100644 index 0000000000..847a500e42 --- /dev/null +++ b/src/babel/transformation/templates/default-parameter-assign.js @@ -0,0 +1 @@ +if (VARIABLE_NAME === undefined) VARIABLE_NAME = DEFAULT_VALUE; diff --git a/src/babel/transformation/transformers/es6/parameters.default.js b/src/babel/transformation/transformers/es6/parameters.default.js index 547c91b754..04f6f7391c 100644 --- a/src/babel/transformation/transformers/es6/parameters.default.js +++ b/src/babel/transformation/transformers/es6/parameters.default.js @@ -1,4 +1,5 @@ import callDelegate from "../../helpers/call-delegate"; +import getFunctionArity from "../../helpers/get-function-arity"; import * as util from "../../../util"; import * as t from "../../../types"; @@ -24,37 +25,54 @@ var iifeVisitor = { export function Func/*tion*/(node, parent, scope, file) { if (!hasDefaults(node)) return; + // ensure it's a block, useful for arrow functions t.ensureBlock(node); + var state = { + iife: false, + scope: scope + }; + var body = []; + // var argsIdentifier = t.identifier("arguments"); argsIdentifier._shadowedFunctionLiteral = true; - var lastNonDefaultParam = 0; - - var state = { iife: false, scope: scope }; - - var pushDefNode = function (left, right, i) { - var defNode = util.template("default-parameter", { - VARIABLE_NAME: left, - DEFAULT_VALUE: right, - ARGUMENT_KEY: t.literal(i), - ARGUMENTS: argsIdentifier - }, true); + // push a default parameter definition + function pushDefNode(left, right, i) { + var defNode; + if (exceedsLastNonDefault(i) || t.isPattern(left) || file.transformers["es6.spec.blockScoping"].canTransform()) { + defNode = util.template("default-parameter", { + VARIABLE_NAME: left, + DEFAULT_VALUE: right, + ARGUMENT_KEY: t.literal(i), + ARGUMENTS: argsIdentifier + }, true); + } else { + defNode = util.template("default-parameter-assign", { + VARIABLE_NAME: left, + DEFAULT_VALUE: right + }, true); + } defNode._blockHoist = node.params.length - i; body.push(defNode); - }; + } + // check if an index exceeds the functions arity + function exceedsLastNonDefault(i) { + return i + 1 > lastNonDefaultParam; + } + + // + var lastNonDefaultParam = getFunctionArity(node); + + // var params = this.get("params"); for (var i = 0; i < params.length; i++) { var param = params[i]; if (!param.isAssignmentPattern()) { - if (!param.isRestElement()) { - lastNonDefaultParam = i + 1; - } - if (!param.isIdentifier()) { param.traverse(iifeVisitor, state); } @@ -69,9 +87,13 @@ export function Func/*tion*/(node, parent, scope, file) { var left = param.get("left"); var right = param.get("right"); - var placeholder = scope.generateUidIdentifier("x"); - placeholder._isDefaultPlaceholder = true; - node.params[i] = placeholder; + if (exceedsLastNonDefault(i) || left.isPattern()) { + var placeholder = scope.generateUidIdentifier("x"); + placeholder._isDefaultPlaceholder = true; + node.params[i] = placeholder; + } else { + node.params[i] = left.node; + } if (!state.iife) { if (right.isIdentifier() && scope.hasOwnBinding(right.node.name)) { diff --git a/test/core/fixtures/transformation/es6.parameters.default/before-last/actual.js b/test/core/fixtures/transformation/es6.parameters.default/before-last/actual.js new file mode 100644 index 0000000000..d5f34c085b --- /dev/null +++ b/test/core/fixtures/transformation/es6.parameters.default/before-last/actual.js @@ -0,0 +1,3 @@ +function foo(a = "foo", b) { + +} diff --git a/test/core/fixtures/transformation/es6.parameters.default/before-last/expected.js b/test/core/fixtures/transformation/es6.parameters.default/before-last/expected.js new file mode 100644 index 0000000000..cc754fd872 --- /dev/null +++ b/test/core/fixtures/transformation/es6.parameters.default/before-last/expected.js @@ -0,0 +1,5 @@ +"use strict"; + +function foo(a, b) { + if (a === undefined) a = "foo"; +}