From 5dabe50ed76c4379861c55f99cd5c74788302ad0 Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Fri, 3 Oct 2014 10:50:53 +1000 Subject: [PATCH] add support for break and continue in block binding --- lib/6to5/transformers/block-binding.js | 43 +++++++++++++++---- lib/6to5/traverse/index.js | 17 +++++--- .../for-in-block-wrap-break/actual.js | 6 +++ .../for-in-block-wrap-break/expected.js | 16 +++++++ .../for-in-block-wrap-continue/actual.js | 6 +++ .../for-in-block-wrap-continue/expected.js | 14 ++++++ 6 files changed, 89 insertions(+), 13 deletions(-) create mode 100644 test/fixtures/block-binding/for-in-block-wrap-break/actual.js create mode 100644 test/fixtures/block-binding/for-in-block-wrap-break/expected.js create mode 100644 test/fixtures/block-binding/for-in-block-wrap-continue/actual.js create mode 100644 test/fixtures/block-binding/for-in-block-wrap-continue/expected.js diff --git a/lib/6to5/transformers/block-binding.js b/lib/6to5/transformers/block-binding.js index ca7104dae1..31a3e53c66 100644 --- a/lib/6to5/transformers/block-binding.js +++ b/lib/6to5/transformers/block-binding.js @@ -3,6 +3,8 @@ var util = require("../util"); var b = require("ast-types").builders; var _ = require("lodash"); +var funcTypes = ["FunctionDeclaration", "FunctionExpression"]; + var isLet = function (node) { if (node && node.type === "VariableDeclaration" && node.kind === "let") { node.kind = "var"; @@ -22,9 +24,7 @@ var hasLet = function (nodes) { }; exports.Program = function (node) { - if (hasLet(node.body)) { - node.body = buildNode(node.body); - } + if (hasLet(node.body)) node.body = buildNode(node.body); }; exports.BlockStatement = function (node, parent) { @@ -33,7 +33,31 @@ exports.BlockStatement = function (node, parent) { // ignore if we're the body of a closure already if (parent.type === "FunctionExpression") return; - node.body = buildNode(node.body); + var body = node.body; + node.body = buildNode(node.body, true); + + var container = _.last(node.body); + traverse.replace(container, function (node) { + if (node.type === "ContinueStatement") { + return b.returnStatement(null); + } + }, funcTypes); + + if (traverse.hasType(body, "BreakStatement", funcTypes)) { + var id = b.identifier("_break"); + + node.body.unshift(b.variableDeclaration("var", [ + b.variableDeclarator(id, b.literal(false)) + ])); + + traverse.replace(container, function (node) { + if (node.type === "BreakStatement") { + return b.returnStatement(b.assignmentExpression("=", id, b.literal(true))); + } + }, funcTypes); + + node.body.push(b.ifStatement(id, b.breakStatement())); + } }; exports.ForOfStatement = @@ -45,7 +69,7 @@ exports.ForStatement = function (node) { if (isLet(node.init)) return buildNode(node); }; -var buildNode = function (node) { +var buildNode = function (node, isBlock) { var nodes = []; // hoist normal variable declarations @@ -55,9 +79,11 @@ var buildNode = function (node) { if (node._ignoreBlockBindingHoist) return node; if (node.type === "VariableDeclaration" && node.kind === "var") { - nodes.push(b.variableDeclaration("var", node.declarations.map(function (declar) { + var declars = node.declarations.map(function (declar) { return b.variableDeclarator(declar.id, null); - }))); + }); + + nodes.push(b.variableDeclaration("var", declars)); return node.declarations.map(function (declar) { return util.template("assign", { @@ -80,7 +106,8 @@ var buildNode = function (node) { var block = b.blockStatement([]); block.body = node; - var func = b.functionExpression(null, [], block, false) + + var func = b.functionExpression(null, [], block, false); var templateName = "function-call"; if (traverse.hasType(node, "ThisExpression")) { diff --git a/lib/6to5/traverse/index.js b/lib/6to5/traverse/index.js index 6aa1f28fed..164983728f 100644 --- a/lib/6to5/traverse/index.js +++ b/lib/6to5/traverse/index.js @@ -1,15 +1,17 @@ var VISITOR_KEYS = require("./visitor-keys"); var _ = require("lodash"); -var traverse = module.exports = function (parent, callback) { +var traverse = module.exports = function (parent, callback, blacklistTypes) { + if (_.isArray(parent)) { _.each(parent, function (node) { - traverse(node, callback); + traverse(node, callback, blacklistTypes); }); return; } var keys = VISITOR_KEYS[parent.type] || []; + blacklistTypes = blacklistTypes || []; _.each(keys, function (key) { var nodes = parent[key]; @@ -20,6 +22,7 @@ var traverse = module.exports = function (parent, callback) { // strict references in case the callback modified/replaced the node + if (blacklistTypes.indexOf(obj[key].type) >= 0) return; var result = callback(obj[key], parent, obj, key); if (result === false) return; @@ -47,12 +50,15 @@ var traverse = module.exports = function (parent, callback) { traverse.Delete = {}; -traverse.hasType = function (tree, type) { +traverse.hasType = function (tree, type, blacklistTypes) { + if (tree.type === type) return true; + var has = false; + blacklistTypes = blacklistTypes || [] if (_.isArray(tree)) { return !!_.find(tree, function (node) { - return traverse.hasType(node, type); + return traverse.hasType(node, type, blacklistTypes); }); } else { traverse(tree, function (node) { @@ -60,7 +66,7 @@ traverse.hasType = function (tree, type) { has = true; return false; } - }); + }, blacklistTypes); } return has; @@ -69,6 +75,7 @@ traverse.hasType = function (tree, type) { traverse.replace = function (node, callback) { traverse(node, function (node, parent, obj, key) { var result = callback(node, parent); + if (result === false) return false; if (result != null) obj[key] = result; }); }; diff --git a/test/fixtures/block-binding/for-in-block-wrap-break/actual.js b/test/fixtures/block-binding/for-in-block-wrap-break/actual.js new file mode 100644 index 0000000000..1d4c41d2a6 --- /dev/null +++ b/test/fixtures/block-binding/for-in-block-wrap-break/actual.js @@ -0,0 +1,6 @@ +var arr = [1, 2, 3]; +for (let i in arr) { + let val = arr[i]; + console.log(val * 2); + break; +} diff --git a/test/fixtures/block-binding/for-in-block-wrap-break/expected.js b/test/fixtures/block-binding/for-in-block-wrap-break/expected.js new file mode 100644 index 0000000000..32919ef144 --- /dev/null +++ b/test/fixtures/block-binding/for-in-block-wrap-break/expected.js @@ -0,0 +1,16 @@ +var arr = [ + 1, + 2, + 3 +]; +(function () { + for (var i in arr) { + var _break = false; + (function () { + var val = arr[i]; + console.log(val * 2); + return _break = true; + }()); + if (_break) break; + } +}()); diff --git a/test/fixtures/block-binding/for-in-block-wrap-continue/actual.js b/test/fixtures/block-binding/for-in-block-wrap-continue/actual.js new file mode 100644 index 0000000000..e4929dc916 --- /dev/null +++ b/test/fixtures/block-binding/for-in-block-wrap-continue/actual.js @@ -0,0 +1,6 @@ +var arr = [1, 2, 3]; +for (let i in arr) { + let val = arr[i]; + console.log(val * 2); + continue; +} diff --git a/test/fixtures/block-binding/for-in-block-wrap-continue/expected.js b/test/fixtures/block-binding/for-in-block-wrap-continue/expected.js new file mode 100644 index 0000000000..0281eb9c40 --- /dev/null +++ b/test/fixtures/block-binding/for-in-block-wrap-continue/expected.js @@ -0,0 +1,14 @@ +var arr = [ + 1, + 2, + 3 +]; +(function () { + for (var i in arr) { + (function () { + var val = arr[i]; + console.log(val * 2); + return; + }()); + } +}());