add better support for labels in the block scoping transformer and add more let scoping tests - fixes #644 and closes #608
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
var traverse = require("../../../traverse");
|
||||
var object = require("../../../helpers/object");
|
||||
var clone = require("lodash/lang/clone");
|
||||
var util = require("../../../util");
|
||||
var t = require("../../../types");
|
||||
var values = require("lodash/object/values");
|
||||
@@ -308,34 +309,57 @@ LetScoping.prototype.getLetReferences = function () {
|
||||
return state.closurify;
|
||||
};
|
||||
|
||||
var loopNodeTo = function (node) {
|
||||
if (t.isBreakStatement(node)) {
|
||||
return "break";
|
||||
} else if (t.isContinueStatement(node)) {
|
||||
return "continue";
|
||||
}
|
||||
};
|
||||
|
||||
var loopVisitor = {
|
||||
enter: function (node, parent, scope, context, state) {
|
||||
var replace;
|
||||
|
||||
if (t.isLoop(node)) {
|
||||
state = clone(state);
|
||||
state.ignoreLabeless = true;
|
||||
traverse(node, loopVisitor, scope, state);
|
||||
}
|
||||
|
||||
if (t.isFunction(node) || t.isLoop(node)) {
|
||||
return context.skip();
|
||||
}
|
||||
|
||||
if (node && !node.label && state.isLoop) {
|
||||
if (t.isBreakStatement(node)) {
|
||||
if (t.isSwitchCase(parent)) return;
|
||||
var loopText = loopNodeTo(node);
|
||||
|
||||
state.hasBreak = true;
|
||||
replace = t.returnStatement(t.literal("break"));
|
||||
} else if (t.isContinueStatement(node)) {
|
||||
state.hasContinue = true;
|
||||
replace = t.returnStatement(t.literal("continue"));
|
||||
if (loopText) {
|
||||
if (node.label) {
|
||||
loopText = loopText + "|" + node.label.name;
|
||||
} else {
|
||||
// we shouldn't be dealing with this
|
||||
if (state.ignoreLabeless) return;
|
||||
|
||||
// break statements mean something different in this context
|
||||
if (t.isBreakStatement(node) && t.isSwitchCase(parent)) return;
|
||||
}
|
||||
|
||||
state.hasBreakContinue = true;
|
||||
state.map[loopText] = node;
|
||||
replace = t.literal(loopText);
|
||||
}
|
||||
|
||||
if (t.isReturnStatement(node)) {
|
||||
state.hasReturn = true;
|
||||
replace = t.returnStatement(t.objectExpression([
|
||||
replace = t.objectExpression([
|
||||
t.property("init", t.identifier("v"), node.argument || t.identifier("undefined"))
|
||||
]));
|
||||
]);
|
||||
}
|
||||
|
||||
if (replace) return t.inherits(replace, node);
|
||||
if (replace) {
|
||||
replace = t.returnStatement(replace);
|
||||
return t.inherits(replace, node);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -350,10 +374,11 @@ var loopVisitor = {
|
||||
|
||||
LetScoping.prototype.checkLoop = function () {
|
||||
var state = {
|
||||
hasContinue: false,
|
||||
hasReturn: false,
|
||||
hasBreak: false,
|
||||
isLoop: !!this.loopParent
|
||||
hasBreakContinue: false,
|
||||
ignoreLabeless: false,
|
||||
hasReturn: false,
|
||||
isLoop: !!this.loopParent,
|
||||
map: {}
|
||||
};
|
||||
|
||||
traverse(this.block, loopVisitor, this.scope, state);
|
||||
@@ -422,7 +447,7 @@ LetScoping.prototype.pushDeclar = function (node) {
|
||||
|
||||
LetScoping.prototype.build = function (ret, call) {
|
||||
var has = this.has;
|
||||
if (has.hasReturn || has.hasBreak || has.hasContinue) {
|
||||
if (has.hasReturn || has.hasBreakContinue) {
|
||||
this.buildHas(ret, call);
|
||||
} else {
|
||||
this.body.push(t.expressionStatement(call));
|
||||
@@ -455,22 +480,14 @@ LetScoping.prototype.buildHas = function (ret, call) {
|
||||
});
|
||||
}
|
||||
|
||||
if (has.hasBreak || has.hasContinue) {
|
||||
if (has.hasBreakContinue) {
|
||||
if (!loopParent) {
|
||||
throw new Error("Has no loop parent but we're trying to reassign breaks " +
|
||||
"and continues, something is going wrong here.");
|
||||
}
|
||||
|
||||
// ensure that the parent has a label as we're building a switch and we
|
||||
// need to be able to access it
|
||||
var label = loopParent.label = loopParent.label || this.scope.generateUidIdentifier("loop");
|
||||
|
||||
if (has.hasBreak) {
|
||||
cases.push(t.switchCase(t.literal("break"), [t.breakStatement(label)]));
|
||||
}
|
||||
|
||||
if (has.hasContinue) {
|
||||
cases.push(t.switchCase(t.literal("continue"), [t.continueStatement(label)]));
|
||||
for (var key in has.map) {
|
||||
cases.push(t.switchCase(t.literal(key), [has.map[key]]));
|
||||
}
|
||||
|
||||
if (has.hasReturn) {
|
||||
|
||||
15
test/fixtures/transformation/es6-block-scoping-exec/label.js
vendored
Normal file
15
test/fixtures/transformation/es6-block-scoping-exec/label.js
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
var heh = [];
|
||||
var nums = [1, 2, 3];
|
||||
|
||||
loop1:
|
||||
for (let i in nums) {
|
||||
let num = nums[i];
|
||||
heh.push(x => x * num);
|
||||
if (num >= 2) {
|
||||
break loop1;
|
||||
}
|
||||
}
|
||||
|
||||
assert.equal(heh.length, 2);
|
||||
assert.equal(heh[0](2), 2);
|
||||
assert.equal(heh[1](4), 8);
|
||||
4
test/fixtures/transformation/es6-block-scoping-exec/multiple.js
vendored
Normal file
4
test/fixtures/transformation/es6-block-scoping-exec/multiple.js
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
for (let i = 0, x = 2; i < 5; i++);
|
||||
|
||||
assert.ok(typeof i === "undefined");
|
||||
assert.ok(typeof x === "undefined");
|
||||
22
test/fixtures/transformation/es6-block-scoping-exec/nested-labels-2.js
vendored
Normal file
22
test/fixtures/transformation/es6-block-scoping-exec/nested-labels-2.js
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
(function () {
|
||||
var stack = [];
|
||||
|
||||
loop1:
|
||||
for (let j = 0; j < 10; j++) {
|
||||
for (let i = 0; i < 10; i++) {
|
||||
stack.push(() => [i, j]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert.deepEqual(stack[0](), [0, 0]);
|
||||
assert.deepEqual(stack[1](), [0, 1]);
|
||||
assert.deepEqual(stack[2](), [0, 2]);
|
||||
assert.deepEqual(stack[3](), [0, 3]);
|
||||
assert.deepEqual(stack[4](), [0, 4]);
|
||||
assert.deepEqual(stack[5](), [0, 5]);
|
||||
assert.deepEqual(stack[6](), [0, 6]);
|
||||
assert.deepEqual(stack[7](), [0, 7]);
|
||||
assert.deepEqual(stack[8](), [0, 8]);
|
||||
assert.deepEqual(stack[9](), [0, 9]);
|
||||
})();
|
||||
22
test/fixtures/transformation/es6-block-scoping-exec/nested-labels.js
vendored
Normal file
22
test/fixtures/transformation/es6-block-scoping-exec/nested-labels.js
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
(function () {
|
||||
var stack = [];
|
||||
|
||||
loop1:
|
||||
for (let j = 0; j < 10; j++) {
|
||||
for (let i = 0; i < 10; i++) {
|
||||
stack.push(() => [i, j]);
|
||||
continue loop1;
|
||||
}
|
||||
}
|
||||
|
||||
assert.deepEqual(stack[0](), [0, 0]);
|
||||
assert.deepEqual(stack[1](), [0, 1]);
|
||||
assert.deepEqual(stack[2](), [0, 2]);
|
||||
assert.deepEqual(stack[3](), [0, 3]);
|
||||
assert.deepEqual(stack[4](), [0, 4]);
|
||||
assert.deepEqual(stack[5](), [0, 5]);
|
||||
assert.deepEqual(stack[6](), [0, 6]);
|
||||
assert.deepEqual(stack[7](), [0, 7]);
|
||||
assert.deepEqual(stack[8](), [0, 8]);
|
||||
assert.deepEqual(stack[9](), [0, 9]);
|
||||
})();
|
||||
@@ -1,7 +1,7 @@
|
||||
"use strict";
|
||||
|
||||
(function () {
|
||||
_loop: for (var i in nums) {
|
||||
for (var i in nums) {
|
||||
var _ret = (function (i) {
|
||||
fns.push(function () {
|
||||
return i;
|
||||
@@ -18,10 +18,10 @@
|
||||
})(i);
|
||||
|
||||
switch (_ret) {
|
||||
case "break":
|
||||
break _loop;
|
||||
case "continue":
|
||||
continue _loop;
|
||||
continue;
|
||||
case "break":
|
||||
break;
|
||||
default:
|
||||
if (typeof _ret === "object") return _ret.v;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use strict";
|
||||
|
||||
_loop: for (var i in nums) {
|
||||
for (var i in nums) {
|
||||
var _ret = (function (i) {
|
||||
fns.push(function () {
|
||||
return i;
|
||||
@@ -8,5 +8,5 @@ _loop: for (var i in nums) {
|
||||
return "break";
|
||||
})(i);
|
||||
|
||||
if (_ret === "break") break _loop;
|
||||
if (_ret === "break") break;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use strict";
|
||||
|
||||
_loop: for (var i in nums) {
|
||||
for (var i in nums) {
|
||||
var _ret = (function (i) {
|
||||
fns.push(function () {
|
||||
return i;
|
||||
@@ -8,5 +8,5 @@ _loop: for (var i in nums) {
|
||||
return "continue";
|
||||
})(i);
|
||||
|
||||
if (_ret === "continue") continue _loop;
|
||||
if (_ret === "continue") continue;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user