From c7d69b2f92cf9aa26cafc2ace3cdf29acaee0bba Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Wed, 10 Dec 2014 23:49:13 +1100 Subject: [PATCH] block scope constants --- .../transformers/es6-constants.js | 20 ++++++++++++++++--- lib/6to5/traverse/scope.js | 17 +++++++++------- .../es6-constants/block-scoped/exec.js | 3 +++ 3 files changed, 30 insertions(+), 10 deletions(-) create mode 100644 test/fixtures/transformation/es6-constants/block-scoped/exec.js diff --git a/lib/6to5/transformation/transformers/es6-constants.js b/lib/6to5/transformation/transformers/es6-constants.js index 15f2c8c45b..2996974c37 100644 --- a/lib/6to5/transformation/transformers/es6-constants.js +++ b/lib/6to5/transformation/transformers/es6-constants.js @@ -9,11 +9,21 @@ exports.ForOfStatement = exports.ForStatement = function (node, parent, file) { var constants = {}; - var check = function (parent, names) { + /** + * Check the results of `util.getIds` as `names` generated from a + * node against it's parent. + */ + + var check = function (parent, names, scope) { _.each(names, function (nameNode, name) { if (!_.has(constants, name)) return; if (parent && t.isBlockStatement(parent) && parent !== constants[name]) return; + if (scope) { + var defined = scope.get(name); + if (defined && defined === nameNode) return; + } + throw file.errorWithNode(nameNode, name + " is read-only"); }); }; @@ -22,6 +32,10 @@ exports.ForStatement = function (node, parent, file) { return t.getIds(node, true, ["MemberExpression"]); }; + /** + * Collect all constants in this scope. + */ + _.each(node.body, function (child, parent) { if (child && t.isVariableDeclaration(child, { kind: "const" })) { _.each(child.declarations, function (declar) { @@ -43,12 +57,12 @@ exports.ForStatement = function (node, parent, file) { if (_.isEmpty(constants)) return; - traverse(node, function (child, parent) { + traverse(node, function (child, parent, scope) { if (child._ignoreConstant) return; if (t.isVariableDeclaration(child)) return; if (t.isVariableDeclarator(child) || t.isDeclaration(child) || t.isAssignmentExpression(child)) { - check(parent, getIds(child)); + check(parent, getIds(child), scope); } }); }; diff --git a/lib/6to5/traverse/scope.js b/lib/6to5/traverse/scope.js index 50bb67243c..b2faeab838 100644 --- a/lib/6to5/traverse/scope.js +++ b/lib/6to5/traverse/scope.js @@ -27,12 +27,12 @@ Scope.add = function (node, references) { }; Scope.prototype.generateTemp = function (file, name) { - var id = file.generateUidIdentifier(name || "temp", this); - this.push({ - key: id.name, - id: id - }); - return id; + var id = file.generateUidIdentifier(name || "temp", this); + this.push({ + key: id.name, + id: id + }); + return id; }; Scope.prototype.getReferences = function () { @@ -86,12 +86,15 @@ Scope.prototype.getReferences = function () { // declared within are accessible if (t.isFunction(node)) return false; + // function identifier doesn't belong to this scope + if (block.id && node === block.id) return; + if (t.isIdentifier(node) && t.isReferenced(node, parent) && !scope.has(node.name)) { add(node); } // we've ran into a declaration! - // we'll let the BlockStatement scope deal with `let` declarations + // we'll let the BlockStatement scope deal with `let` declarations unless if (t.isDeclaration(node) && !t.isLet(node)) { add(node); } diff --git a/test/fixtures/transformation/es6-constants/block-scoped/exec.js b/test/fixtures/transformation/es6-constants/block-scoped/exec.js new file mode 100644 index 0000000000..c46d684d19 --- /dev/null +++ b/test/fixtures/transformation/es6-constants/block-scoped/exec.js @@ -0,0 +1,3 @@ +const bar = 123; +{ const bar = 456; } +assert.equal(bar, 123);