From 49578fe2237f87470c7e64451ce0b602c830d83b Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Tue, 16 Dec 2014 08:07:46 +1100 Subject: [PATCH] fix let variable declaration hoisting bug. All let variable declarators now have a default initializer of `undefined` unless they're the left of a `ForIn` or `ForOf` --- .../transformers/es6-let-scoping.js | 57 ++++++------------- 1 file changed, 18 insertions(+), 39 deletions(-) diff --git a/lib/6to5/transformation/transformers/es6-let-scoping.js b/lib/6to5/transformation/transformers/es6-let-scoping.js index 4e209b37d6..bfcd1e6e78 100644 --- a/lib/6to5/transformation/transformers/es6-let-scoping.js +++ b/lib/6to5/transformation/transformers/es6-let-scoping.js @@ -3,18 +3,25 @@ var util = require("../../util"); var t = require("../../types"); var _ = require("lodash"); -var isLet = function (node) { +var isLet = function (node, parent) { if (!t.isVariableDeclaration(node)) return false; if (node._let) return true; if (node.kind !== "let") return false; + // https://github.com/6to5/6to5/issues/255 + if (!t.isFor(parent) || t.isFor(parent) && parent.left !== node) { + _.each(node.declarations, function (declar) { + declar.init = declar.init || t.identifier("undefined"); + }); + } + node._let = true; node.kind = "var"; return true; }; -var isVar = function (node) { - return t.isVariableDeclaration(node, { kind: "var" }) && !isLet(node); +var isVar = function (node, parent) { + return t.isVariableDeclaration(node, { kind: "var" }) && !isLet(node, parent); }; var standardiseLets = function (declars) { @@ -23,13 +30,13 @@ var standardiseLets = function (declars) { } }; -exports.VariableDeclaration = function (node) { - isLet(node); +exports.VariableDeclaration = function (node, parent) { + isLet(node, parent); }; exports.Loop = function (node, parent, file, scope) { var init = node.left || node.init; - if (isLet(init)) { + if (isLet(init, node)) { t.ensureBlock(node); node.body._letDeclars = [init]; } @@ -91,9 +98,6 @@ LetScoping.prototype.run = function () { // remap all let references that exist in upper scopes to their uid this.remap(); - // add default initializer to let variables in loop bodys - this.initialiseLoopLets(); - // this is a block within a `Function` so we can safely leave it be if (t.isFunction(this.parent)) return this.noClosure(); @@ -242,7 +246,7 @@ LetScoping.prototype.getInfo = function () { for (var i in block.body) { var declar = block.body[i]; - if (!isLet(declar)) continue; + if (!isLet(declar, block)) continue; _.each(t.getIds(declar, true), function (id, key) { duplicates(id, key); @@ -253,31 +257,6 @@ LetScoping.prototype.getInfo = function () { return opts; }; -/** - * Any let variable declared within a loop body need to have an initializer or - * else they'll be hoisted and subsequent iterations of the loop will have a - * previous state. This function adds a default initializer of `undefined` to - * those variables. - */ - -LetScoping.prototype.initialiseLoopLets = function () { - var loopParent = this.loopParent; - if (!loopParent) return; - - traverse(this.block, function (node) { - if (t.isFunction(node) || t.isLoop(node)) { - return false; - } - - if (isLet(node)) { - for (var i in node.declarations) { - var declar = node.declarations[i]; - declar.init = declar.init || t.identifier("undefined"); - } - } - }); -}; - /** * If we're inside of a loop then traverse it and check if it has one of * the following node types `ReturnStatement`, `BreakStatement`, @@ -333,16 +312,16 @@ LetScoping.prototype.checkLoop = function () { LetScoping.prototype.hoistVarDeclarations = function () { var self = this; - traverse(this.block, function (node) { + traverse(this.block, function (node, parent) { if (t.isForStatement(node)) { - if (isVar(node.init)) { + if (isVar(node.init, node)) { node.init = t.sequenceExpression(self.pushDeclar(node.init)); } } else if (t.isFor(node)) { - if (isVar(node.left)) { + if (isVar(node.left, node)) { node.left = node.left.declarations[0].id; } - } else if (isVar(node)) { + } else if (isVar(node, parent)) { return self.pushDeclar(node).map(t.expressionStatement); } else if (t.isFunction(node)) { return false;