Compare commits

...

5 Commits

Author SHA1 Message Date
Sebastian McKenzie
d877a04397 v2.13.1 2015-01-18 21:40:21 +11:00
Sebastian McKenzie
4844882f5e break let scoping transformer if there are no block scoped references 2015-01-18 21:37:37 +11:00
Sebastian McKenzie
a80945cfb4 ignore function declarations in TDZ detection 2015-01-18 21:33:22 +11:00
Sebastian McKenzie
6a884c58a7 add 2.13.1 changelog 2015-01-18 21:28:34 +11:00
Sebastian McKenzie
7c4701716c implement block scoping TDZ 2015-01-18 21:26:02 +11:00
6 changed files with 70 additions and 4 deletions

View File

@@ -11,6 +11,11 @@
_Note: Gaps between patch versions are faulty/broken releases._
## 2.13.1
* **New Feature**
* Temporal dead zone for block binding.
## 2.13.0
* **New Feature**

View File

@@ -57,6 +57,7 @@ exports.Loop = function (node, parent, scope, context, file) {
}
};
exports.Program =
exports.BlockStatement = function (block, parent, scope, context, file) {
if (!t.isLoop(parent)) {
var letScoping = new LetScoping(false, block, parent, scope, file);
@@ -82,6 +83,7 @@ function LetScoping(loopParent, block, parent, scope, file) {
this.file = file;
this.outsideLetReferences = {};
this.hasLetReferences = false;
this.letReferences = {};
this.body = [];
}
@@ -95,10 +97,15 @@ LetScoping.prototype.run = function () {
if (block._letDone) return;
block._letDone = true;
// this is a block within a `Function` so we can safely leave it be
if (t.isFunction(this.parent)) return;
var needsClosure = this.getLetReferences();
this.checkTDZ();
// this is a block within a `Function/Program` so we can safely leave it be
if (t.isFunction(this.parent) || t.isProgram(this.block)) return;
// we can skip everything
if (!this.hasLetReferences) return;
if (needsClosure) {
this.needsClosure();
} else {
@@ -106,6 +113,48 @@ LetScoping.prototype.run = function () {
}
};
/**
* Description
*/
LetScoping.prototype.checkTDZ = function () {
var state = {
letRefs: this.letReferences,
file: this.file
};
traverse(this.block, {
enter: function (node, parent, scope, context, state) {
if (!t.isIdentifier(node)) return;
if (!t.isReferenced(node, parent)) return;
var declared = state.letRefs[node.name];
if (!declared) return;
// declared node is different in this scope
if (scope.get(node.name, true) !== declared) return;
var declaredLoc = declared.loc;
var referenceLoc = node.loc;
if (!declaredLoc || !referenceLoc) return;
// does this reference appear on a line before the declaration?
var before = referenceLoc.start.line < declaredLoc.start.line;
if (referenceLoc.start.line === declaredLoc.start.line) {
// this reference appears on the same line
// check it appears before the declaration
before = referenceLoc.start.col < declaredLoc.start.col;
}
if (before) {
throw state.file.errorWithNode(node, "Temporal dead zone - accessing a variable before it's initialized");
}
}
}, this.scope, state);
};
/**
* Description
*/
@@ -239,8 +288,12 @@ LetScoping.prototype.getLetReferences = function () {
declar = declarators[i];
var keys = t.getIds(declar, true);
_.extend(this.letReferences, keys);
this.hasLetReferences = true;
}
// no let references so we can just quit
if (!this.hasLetReferences) return;
// set let references to plain var references
standardiseLets(declarators);

View File

@@ -7,6 +7,9 @@ exports.FunctionDeclaration = function (node, parent) {
return;
}
// this is to avoid triggering the TDZ detection
node.id.loc = null;
var declar = t.variableDeclaration("let", [
t.variableDeclarator(node.id, t.toExpression(node))
]);

View File

@@ -1,7 +1,7 @@
{
"name": "6to5",
"description": "Turn ES6 code into readable vanilla ES5 with source maps",
"version": "2.13.0",
"version": "2.13.1",
"author": "Sebastian McKenzie <sebmck@gmail.com>",
"homepage": "https://6to5.org/",
"repository": "6to5/6to5",

View File

@@ -0,0 +1,2 @@
qux;
let qux = 456;

View File

@@ -0,0 +1,3 @@
{
"throws": "Temporal dead zone - accessing a variable before it's initialized"
}