From a1895b4bb443f301ef2ff21e50f11b479c5c284f Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Sun, 16 Nov 2014 14:32:03 +1100 Subject: [PATCH] implement temporal dead zone for default parameters - fixes #169 --- .../transformers/default-parameters.js | 63 ++++++++++++++++--- .../default-parameters/multiple/expected.js | 2 +- .../default-parameters/tdz-2/actual.js | 1 + .../default-parameters/tdz-2/options.json | 3 + .../default-parameters/tdz-3/exec.js | 4 ++ .../default-parameters/tdz/actual.js | 1 + .../default-parameters/tdz/options.json | 3 + 7 files changed, 69 insertions(+), 8 deletions(-) create mode 100644 test/fixtures/transformation/default-parameters/tdz-2/actual.js create mode 100644 test/fixtures/transformation/default-parameters/tdz-2/options.json create mode 100644 test/fixtures/transformation/default-parameters/tdz-3/exec.js create mode 100644 test/fixtures/transformation/default-parameters/tdz/actual.js create mode 100644 test/fixtures/transformation/default-parameters/tdz/options.json diff --git a/lib/6to5/transformation/transformers/default-parameters.js b/lib/6to5/transformation/transformers/default-parameters.js index 5a850e42d8..3c754d434b 100644 --- a/lib/6to5/transformation/transformers/default-parameters.js +++ b/lib/6to5/transformation/transformers/default-parameters.js @@ -1,21 +1,70 @@ -var util = require("../../util"); -var t = require("../../types"); -var _ = require("lodash"); +var traverse = require("../../traverse"); +var util = require("../../util"); +var t = require("../../types"); +var _ = require("lodash"); -exports.Function = function (node) { +exports.Function = function (node, parent, file, scope) { if (!node.defaults || !node.defaults.length) return; t.ensureBlock(node); + var ids = node.params.map(function (param) { + return t.getIds(param); + }); + + var closure = false; + _.each(node.defaults, function (def, i) { if (!def) return; var param = node.params[i]; - node.body.body.unshift(util.template("if-undefined-set-to", { - VARIABLE: param, - DEFAULT: def + // temporal dead zone check - here we prevent accessing of params that + // are to the right - ie. uninitialized parameters + _.each(ids.slice(i), function (ids, i) { + var check = function (node, parent) { + if (!t.isIdentifier(node) || !t.isReferenced(node, parent)) return; + + if (_.contains(ids, node.name)) { + throw file.errorWithNode(node, "Temporal dead zone - accessing a variable before it's initialized"); + } + + if (scope.has(node.name)) { + closure = true; + } + }; + + check(def, node); + traverse(def, check); + }); + + // we're accessing a variable that's already defined within this function + var has = scope.get(param.name); + if (has && !_.contains(node.params, has)) { + closure = true; + } + }); + + var body = []; + + _.each(node.defaults, function (def, i) { + if (!def) return; + + body.push(util.template("if-undefined-set-to", { + VARIABLE: node.params[i], + DEFAULT: def }, true)); }); + if (closure) { + var container = t.functionExpression(null, [], node.body, node.generator); + container._aliasFunction = true; + + body.push(t.returnStatement(t.callExpression(container, []))); + + node.body = t.blockStatement(body); + } else { + node.body.body = body.concat(node.body.body); + } + node.defaults = []; }; diff --git a/test/fixtures/transformation/default-parameters/multiple/expected.js b/test/fixtures/transformation/default-parameters/multiple/expected.js index 48cb3f0db9..55bc52c5c9 100644 --- a/test/fixtures/transformation/default-parameters/multiple/expected.js +++ b/test/fixtures/transformation/default-parameters/multiple/expected.js @@ -1,8 +1,8 @@ "use strict"; var t = function (t, f) { - if (f === undefined) f = 5; if (t === undefined) t = "foo"; + if (f === undefined) f = 5; return t + " bar " + f; }; diff --git a/test/fixtures/transformation/default-parameters/tdz-2/actual.js b/test/fixtures/transformation/default-parameters/tdz-2/actual.js new file mode 100644 index 0000000000..4249062965 --- /dev/null +++ b/test/fixtures/transformation/default-parameters/tdz-2/actual.js @@ -0,0 +1 @@ +function foo(a=b, b) {} diff --git a/test/fixtures/transformation/default-parameters/tdz-2/options.json b/test/fixtures/transformation/default-parameters/tdz-2/options.json new file mode 100644 index 0000000000..faba38c879 --- /dev/null +++ b/test/fixtures/transformation/default-parameters/tdz-2/options.json @@ -0,0 +1,3 @@ +{ + "throws": "Temporal dead zone - accessing a variable before it's initialized" +} diff --git a/test/fixtures/transformation/default-parameters/tdz-3/exec.js b/test/fixtures/transformation/default-parameters/tdz-3/exec.js new file mode 100644 index 0000000000..7d4a351ea1 --- /dev/null +++ b/test/fixtures/transformation/default-parameters/tdz-3/exec.js @@ -0,0 +1,4 @@ +assert.equal((function(a, b=a++){ + function b(){} + return a; +})(1), 2); diff --git a/test/fixtures/transformation/default-parameters/tdz/actual.js b/test/fixtures/transformation/default-parameters/tdz/actual.js new file mode 100644 index 0000000000..3095e9c769 --- /dev/null +++ b/test/fixtures/transformation/default-parameters/tdz/actual.js @@ -0,0 +1 @@ +function foo(a=a) {} diff --git a/test/fixtures/transformation/default-parameters/tdz/options.json b/test/fixtures/transformation/default-parameters/tdz/options.json new file mode 100644 index 0000000000..faba38c879 --- /dev/null +++ b/test/fixtures/transformation/default-parameters/tdz/options.json @@ -0,0 +1,3 @@ +{ + "throws": "Temporal dead zone - accessing a variable before it's initialized" +}