diff --git a/lib/6to5/to-fast-properties.js b/lib/6to5/to-fast-properties.js new file mode 100644 index 0000000000..341a451d03 --- /dev/null +++ b/lib/6to5/to-fast-properties.js @@ -0,0 +1,14 @@ +/** + * A trick from Bluebird to force V8 to use fast properties for an object. + * Read more: http://stackoverflow.com/questions/24987896/ + * + * Use %HasFastProperties(obj) and --allow-natives-syntax to check whether + * a particular object already has fast properties. + */ +module.exports = function toFastProperties(obj) { + /*jshint -W027*/ + function f() {} + f.prototype = obj; + return f; + eval(obj); +}; diff --git a/lib/6to5/transformation/helpers/name-method.js b/lib/6to5/transformation/helpers/name-method.js index 00d3c77a08..36cdbddaea 100644 --- a/lib/6to5/transformation/helpers/name-method.js +++ b/lib/6to5/transformation/helpers/name-method.js @@ -2,6 +2,24 @@ var traverse = require("../../traverse"); var util = require("../../util"); var t = require("../../types"); +var traverser = { + enter: function (node, parent, scope, context, state) { + // check if this node is an identifier that matches the same as our function id + if (!t.isIdentifier(node, { name: state.id })) return; + + // check if this node is the one referenced + if (!t.isReferenced(node, parent)) return; + + // check that we don't have a local variable declared as that removes the need + // for the wrapper + var localDeclar = scope.get(state.id, true); + if (localDeclar !== state.outerDeclar) return; + + state.selfReference = true; + context.stop(); + } +}; + module.exports = function (node, file, scope) { var key = t.toComputedKey(node, node.key); if (!t.isLiteral(key)) return node; // we can't set a function id with this @@ -9,28 +27,15 @@ module.exports = function (node, file, scope) { var id = t.toIdentifier(key.value); key = t.identifier(id); - var selfReference = false; - var outerDeclar = scope.get(id, true); + var state = { + id: id, + selfReference: false, + outerDeclar: scope.get(id, true), + }; - traverse(node, { - enter: function (node, parent, scope) { - // check if this node is an identifier that matches the same as our function id - if (!t.isIdentifier(node, { name: id })) return; + traverse(node, traverser, scope, state); - // check if this node is the one referenced - if (!t.isReferenced(node, parent)) return; - - // check that we don't have a local variable declared as that removes the need - // for the wrapper - var localDeclar = scope.get(id, true); - if (localDeclar !== outerDeclar) return; - - selfReference = true; - this.stop(); - } - }, scope); - - if (selfReference) { + if (state.selfReference) { node.value = util.template("property-method-assignment-wrapper", { FUNCTION: node.value, FUNCTION_ID: key, diff --git a/lib/6to5/transformation/helpers/remap-async-to-generator.js b/lib/6to5/transformation/helpers/remap-async-to-generator.js index 86a2c00ba7..7868e86ec7 100644 --- a/lib/6to5/transformation/helpers/remap-async-to-generator.js +++ b/lib/6to5/transformation/helpers/remap-async-to-generator.js @@ -6,8 +6,8 @@ module.exports = function (node, callId) { node.generator = true; traverse(node, { - enter: function (node) { - if (t.isFunction(node)) this.skip(); + enter: function (node, parent, scope, context) { + if (t.isFunction(node)) context.skip(); if (t.isAwaitExpression(node)) { node.type = "YieldExpression"; diff --git a/lib/6to5/transformation/modules/_default.js b/lib/6to5/transformation/modules/_default.js index 42e377b3a8..1485d27f03 100644 --- a/lib/6to5/transformation/modules/_default.js +++ b/lib/6to5/transformation/modules/_default.js @@ -16,35 +16,51 @@ function DefaultFormatter(file) { //this.checkCollisions(); } +var exportsTraverser = { + enter: function (node, parent, scope, context, localExports) { + var declar = node && node.declaration; + if (t.isExportDeclaration(node) && declar && t.isStatement(declar)) { + _.extend(localExports, t.getIds(declar, true)); + } + } +}; + DefaultFormatter.prototype.getLocalExports = function () { var localExports = {}; - - traverse(this.file.ast, { - enter: function (node) { - var declar = node && node.declaration; - if (t.isExportDeclaration(node) && declar && t.isStatement(declar)) { - _.extend(localExports, t.getIds(declar, true)); - } - } - }); - + traverse(this.file.ast, exportsTraverser, null, localExports); return localExports; }; +var importsTraverser = { + enter: function (node, parent, scope, context, localImports) { + if (t.isImportDeclaration(node)) { + _.extend(localImports, t.getIds(node, true)); + } + } +}; + DefaultFormatter.prototype.getLocalImports = function () { var localImports = {}; - - traverse(this.file.ast, { - enter: function (node) { - if (t.isImportDeclaration(node)) { - _.extend(localImports, t.getIds(node, true)); - } - } - }); - + traverse(this.file.ast, importsTraverser, null, localImports); return localImports; }; +var collissionsTraverser = { + enter: function (node, parent, scope, context, check) { + if (t.isAssignmentExpression(node)) { + + var left = node.left; + if (t.isMemberExpression(left)) { + while (left.object) left = left.object; + } + + check(left); + } else if (t.isDeclaration(node)) { + _.each(t.getIds(node, true), check); + } + } +}; + DefaultFormatter.prototype.checkCollisions = function () { // todo: all check export collissions @@ -61,21 +77,7 @@ DefaultFormatter.prototype.checkCollisions = function () { } }; - traverse(file.ast, { - enter: function (node) { - if (t.isAssignmentExpression(node)) { - - var left = node.left; - if (t.isMemberExpression(left)) { - while (left.object) left = left.object; - } - - check(left); - } else if (t.isDeclaration(node)) { - _.each(t.getIds(node, true), check); - } - } - }); + traverse(file.ast, collissionsTraverser, null, check); }; DefaultFormatter.prototype.remapExportAssignment = function (node) { @@ -100,9 +102,9 @@ DefaultFormatter.prototype.remapAssignments = function () { }; traverse(this.file.ast, { - enter: function (node, parent, scope) { + enter: function (node, parent, scope, context, isLocalReference) { if (t.isUpdateExpression(node) && isLocalReference(node.argument, scope)) { - this.skip(); + context.skip(); // expand to long file assignment expression var assign = t.assignmentExpression(node.operator[0] + "=", node.argument, t.literal(1)); @@ -130,11 +132,11 @@ DefaultFormatter.prototype.remapAssignments = function () { } if (t.isAssignmentExpression(node) && isLocalReference(node.left, scope)) { - this.skip(); + context.skip(); return self.remapExportAssignment(node); } } - }); + }, null, isLocalReference); }; DefaultFormatter.prototype.getModuleName = function () { diff --git a/lib/6to5/transformation/modules/system.js b/lib/6to5/transformation/modules/system.js index b70c8423a4..d0650a69c2 100644 --- a/lib/6to5/transformation/modules/system.js +++ b/lib/6to5/transformation/modules/system.js @@ -63,28 +63,31 @@ SystemFormatter.prototype.importSpecifier = function (specifier, node, nodes) { SystemFormatter.prototype.buildRunnerSetters = function (block, hoistDeclarators) { return t.arrayExpression(_.map(this.ids, function (uid, source) { - var nodes = []; + var state = { + nodes: [], + hoistDeclarators: hoistDeclarators + }; traverse(block, { - enter: function (node) { + enter: function (node, parent, scope, context, state) { if (node._importSource === source) { if (t.isVariableDeclaration(node)) { _.each(node.declarations, function (declar) { - hoistDeclarators.push(t.variableDeclarator(declar.id)); - nodes.push(t.expressionStatement( + state.hoistDeclarators.push(t.variableDeclarator(declar.id)); + state.nodes.push(t.expressionStatement( t.assignmentExpression("=", declar.id, declar.init) )); }); } else { - nodes.push(node); + state.nodes.push(node); } - this.remove(); + context.remove(); } } - }); + }, null, state); - return t.functionExpression(null, [uid], t.blockStatement(nodes)); + return t.functionExpression(null, [uid], t.blockStatement(state.nodes)); })); }; @@ -112,10 +115,10 @@ SystemFormatter.prototype.transform = function (ast) { // hoist up all variable declarations traverse(block, { - enter: function (node, parent, scope) { + enter: function (node, parent, scope, context, hoistDeclarators) { if (t.isFunction(node)) { // nothing inside is accessible - return this.skip(); + return context.skip(); } if (t.isVariableDeclaration(node)) { @@ -150,7 +153,8 @@ SystemFormatter.prototype.transform = function (ast) { return nodes; } } - }); + }, null, hoistDeclarators); + if (hoistDeclarators.length) { var hoistDeclar = t.variableDeclaration("var", hoistDeclarators); hoistDeclar._blockHoist = true; @@ -159,15 +163,15 @@ SystemFormatter.prototype.transform = function (ast) { // hoist up function declarations for circular references traverse(block, { - enter: function (node) { - if (t.isFunction(node)) this.skip(); + enter: function (node, parent, scope, context, handlerBody) { + if (t.isFunction(node)) context.skip(); if (t.isFunctionDeclaration(node) || node._blockHoist) { handlerBody.push(node); - this.remove(); + context.remove(); } } - }); + }, null, handlerBody); handlerBody.push(returnStatement); diff --git a/lib/6to5/transformation/transformer.js b/lib/6to5/transformation/transformer.js index f4de701b51..dbcd0f9d10 100644 --- a/lib/6to5/transformation/transformer.js +++ b/lib/6to5/transformation/transformer.js @@ -4,6 +4,25 @@ var traverse = require("../traverse"); var t = require("../types"); var _ = require("lodash"); +function noop() { } + +function enter(node, parent, scope, context, args) { + var fns = args[1][node.type]; + if (!fns) return; + return fns.enter(node, parent, scope, context, args[0]); +} + +function exit(node, parent, scope, context, args) { + var fns = args[1][node.type]; + if (!fns) return; + return fns.exit(node, parent, scope, context, args[0]); +} + +var traverseOpts = { + enter: enter, + exit: exit +}; + function Transformer(key, transformer, opts) { this.manipulateOptions = transformer.manipulateOptions; this.experimental = !!transformer.experimental; @@ -32,6 +51,9 @@ Transformer.prototype.normalise = function (transformer) { if (!_.isObject(fns)) return; + if (!fns.enter) fns.enter = noop; + if (!fns.exit) fns.exit = noop; + transformer[type] = fns; var aliases = t.FLIPPED_ALIAS_KEYS[type]; @@ -54,28 +76,8 @@ Transformer.prototype.astRun = function (file, key) { }; Transformer.prototype.transform = function (file) { - var transformer = this.transformer; - - var build = function (exit) { - return function (node, parent, scope) { - var fns = transformer[node.type]; - if (!fns) return; - - var fn = fns.enter; - if (exit) fn = fns.exit; - if (!fn) return; - - return fn.call(this, node, parent, file, scope); - }; - }; - this.astRun(file, "before"); - - traverse(file.ast, { - enter: build(), - exit: build(true) - }); - + traverse(file.ast, traverseOpts, null, [file, this.transformer]); this.astRun(file, "after"); }; diff --git a/lib/6to5/transformation/transformers/_alias-functions.js b/lib/6to5/transformation/transformers/_alias-functions.js index 5cf9003c84..1d2bf585f6 100644 --- a/lib/6to5/transformation/transformers/_alias-functions.js +++ b/lib/6to5/transformation/transformers/_alias-functions.js @@ -1,57 +1,62 @@ var traverse = require("../../traverse"); var t = require("../../types"); +var functionChildrenTraverser = { + enter: function (node, parent, scope, context, state) { + if (t.isFunction(node) && !node._aliasFunction) { + return context.skip(); + } + + if (node._ignoreAliasFunctions) return context.skip(); + + var getId; + + if (t.isIdentifier(node) && node.name === "arguments") { + getId = state.getArgumentsId; + } else if (t.isThisExpression(node)) { + getId = state.getThisId; + } else { + return; + } + + if (t.isReferenced(node, parent)) return getId(); + } +}; + +var functionTraverser = { + enter: function (node, parent, scope, context, state) { + if (!node._aliasFunction) { + if (t.isFunction(node)) { + // stop traversal of this node as it'll be hit again by this transformer + return context.skip(); + } else { + return; + } + } + + // traverse all child nodes of this function and find `arguments` and `this` + traverse(node, functionChildrenTraverser, null, state); + + return context.skip(); + } +}; + var go = function (getBody, node, file, scope) { var argumentsId; var thisId; - var getArgumentsId = function () { - return argumentsId = argumentsId || file.generateUidIdentifier("arguments", scope); - }; - - var getThisId = function () { - return thisId = thisId || file.generateUidIdentifier("this", scope); + var state = { + getArgumentsId: function () { + return argumentsId = argumentsId || file.generateUidIdentifier("arguments", scope); + }, + getThisId: function () { + return thisId = thisId || file.generateUidIdentifier("this", scope); + } }; // traverse the function and find all alias functions so we can alias // `arguments` and `this` if necessary - traverse(node, { - enter: function (node) { - if (!node._aliasFunction) { - if (t.isFunction(node)) { - // stop traversal of this node as it'll be hit again by this transformer - return this.skip(); - } else { - return; - } - } - - // traverse all child nodes of this function and find `arguments` and `this` - traverse(node, { - enter: function (node, parent) { - if (t.isFunction(node) && !node._aliasFunction) { - return this.skip(); - } - - if (node._ignoreAliasFunctions) return this.skip(); - - var getId; - - if (t.isIdentifier(node) && node.name === "arguments") { - getId = getArgumentsId; - } else if (t.isThisExpression(node)) { - getId = getThisId; - } else { - return; - } - - if (t.isReferenced(node, parent)) return getId(); - } - }); - - return this.skip(); - } - }); + traverse(node, functionTraverser, null, state); var body; @@ -71,14 +76,14 @@ var go = function (getBody, node, file, scope) { } }; -exports.Program = function (node, parent, file, scope) { +exports.Program = function (node, parent, scope, context, file) { go(function () { return node.body; }, node, file, scope); }; exports.FunctionDeclaration = -exports.FunctionExpression = function (node, parent, file, scope) { +exports.FunctionExpression = function (node, parent, scope, context, file) { go(function () { t.ensureBlock(node); return node.body.body; diff --git a/lib/6to5/transformation/transformers/es6-classes.js b/lib/6to5/transformation/transformers/es6-classes.js index dc079ca164..18f24c4031 100644 --- a/lib/6to5/transformation/transformers/es6-classes.js +++ b/lib/6to5/transformation/transformers/es6-classes.js @@ -3,11 +3,11 @@ var traverse = require("../../traverse"); var util = require("../../util"); var t = require("../../types"); -exports.ClassDeclaration = function (node, parent, file, scope) { +exports.ClassDeclaration = function (node, parent, scope, context, file) { return new Class(node, file, scope, true).run(); }; -exports.ClassExpression = function (node, parent, file, scope) { +exports.ClassExpression = function (node, parent, scope, context, file) { if (!node.id) { if (t.isProperty(parent) && parent.value === node && !parent.computed && t.isIdentifier(parent.key)) { // var o = { foo: class {} }; @@ -316,16 +316,16 @@ Class.prototype.replaceSuperReferences = function (methodNode) { function traverseLevel(node, topLevel) { traverse(node, { - enter: function (node, parent) { + enter: function (node, parent, scope, context) { if (t.isFunction(node) && !t.isArrowFunctionExpression(node)) { // we need to call traverseLevel again so we're context aware traverseLevel(node, false); - return this.skip(); + return context.skip(); } if (t.isProperty(node, { method: true }) || t.isMethodDefinition(node)) { // break on object methods - return this.skip(); + return context.skip(); } var getThisReference = function () { diff --git a/lib/6to5/transformation/transformers/es6-computed-property-names.js b/lib/6to5/transformation/transformers/es6-computed-property-names.js index dcfff3b827..3789ca1d23 100644 --- a/lib/6to5/transformation/transformers/es6-computed-property-names.js +++ b/lib/6to5/transformation/transformers/es6-computed-property-names.js @@ -1,6 +1,6 @@ var t = require("../../types"); -exports.ObjectExpression = function (node, parent, file, scope) { +exports.ObjectExpression = function (node, parent, scope, context, file) { var hasComputed = false; for (var i = 0; i < node.properties.length; i++) { diff --git a/lib/6to5/transformation/transformers/es6-constants.js b/lib/6to5/transformation/transformers/es6-constants.js index dc8e0ce49e..096eee9097 100644 --- a/lib/6to5/transformation/transformers/es6-constants.js +++ b/lib/6to5/transformation/transformers/es6-constants.js @@ -6,7 +6,7 @@ exports.Program = exports.BlockStatement = exports.ForInStatement = exports.ForOfStatement = -exports.ForStatement = function (node, parent, file) { +exports.ForStatement = function (node, parent, scope, context, file) { var hasConstants = false; var constants = {}; @@ -69,14 +69,19 @@ exports.ForStatement = function (node, parent, file) { if (!hasConstants) return; + var state = { + check: check, + getIds: getIds + }; + traverse(node, { - enter: function (child, parent, scope) { + enter: function (child, parent, scope, context, state) { if (child._ignoreConstant) return; if (t.isVariableDeclaration(child)) return; if (t.isVariableDeclarator(child) || t.isDeclaration(child) || t.isAssignmentExpression(child)) { - check(parent, getIds(child), scope); + state.check(parent, state.getIds(child), scope); } } - }); + }, null, state); }; diff --git a/lib/6to5/transformation/transformers/es6-default-parameters.js b/lib/6to5/transformation/transformers/es6-default-parameters.js index 0d99b6db8e..4b03d56295 100644 --- a/lib/6to5/transformation/transformers/es6-default-parameters.js +++ b/lib/6to5/transformation/transformers/es6-default-parameters.js @@ -2,7 +2,7 @@ var traverse = require("../../traverse"); var util = require("../../util"); var t = require("../../types"); -exports.Function = function (node, parent, file, scope) { +exports.Function = function (node, parent, scope, context, file) { if (!node.defaults || !node.defaults.length) return; t.ensureBlock(node); diff --git a/lib/6to5/transformation/transformers/es6-destructuring.js b/lib/6to5/transformation/transformers/es6-destructuring.js index 22d016b38f..bcd04ba4b2 100644 --- a/lib/6to5/transformation/transformers/es6-destructuring.js +++ b/lib/6to5/transformation/transformers/es6-destructuring.js @@ -148,7 +148,7 @@ var pushPattern = function (opts) { }; exports.ForInStatement = -exports.ForOfStatement = function (node, parent, file, scope) { +exports.ForOfStatement = function (node, parent, scope, context, file) { var declar = node.left; if (!t.isVariableDeclaration(declar)) return; @@ -174,7 +174,7 @@ exports.ForOfStatement = function (node, parent, file, scope) { block.body = nodes.concat(block.body); }; -exports.Function = function (node, parent, file, scope) { +exports.Function = function (node, parent, scope, context, file) { var nodes = []; var hasDestructuring = false; @@ -205,7 +205,7 @@ exports.Function = function (node, parent, file, scope) { block.body = nodes.concat(block.body); }; -exports.CatchClause = function (node, parent, file, scope) { +exports.CatchClause = function (node, parent, scope, context, file) { var pattern = node.param; if (!t.isPattern(pattern)) return; @@ -223,7 +223,7 @@ exports.CatchClause = function (node, parent, file, scope) { node.body.body = nodes.concat(node.body.body); }; -exports.ExpressionStatement = function (node, parent, file, scope) { +exports.ExpressionStatement = function (node, parent, scope, context, file) { var expr = node.expression; if (expr.type !== "AssignmentExpression") return; @@ -245,7 +245,7 @@ exports.ExpressionStatement = function (node, parent, file, scope) { return nodes; }; -exports.AssignmentExpression = function (node, parent, file, scope) { +exports.AssignmentExpression = function (node, parent, scope, context, file) { if (parent.type === "ExpressionStatement") return; if (!t.isPattern(node.left)) return; @@ -270,7 +270,7 @@ exports.AssignmentExpression = function (node, parent, file, scope) { return t.toSequenceExpression(nodes, scope); }; -exports.VariableDeclaration = function (node, parent, file, scope) { +exports.VariableDeclaration = function (node, parent, scope, context, file) { if (t.isForInStatement(parent) || t.isForOfStatement(parent)) return; var nodes = []; diff --git a/lib/6to5/transformation/transformers/es6-for-of.js b/lib/6to5/transformation/transformers/es6-for-of.js index 61357cfbad..9018e995af 100644 --- a/lib/6to5/transformation/transformers/es6-for-of.js +++ b/lib/6to5/transformation/transformers/es6-for-of.js @@ -1,11 +1,11 @@ var util = require("../../util"); var t = require("../../types"); -exports.ForOfStatement = function (node, parent, file, scope) { +exports.ForOfStatement = function (node, parent, scope, context, file) { var callback = spec; if (file.isLoose("forOf")) callback = loose; - var build = callback(node, parent, file, scope); + var build = callback(node, parent, scope, context, file); var declar = build.declar; var loop = build.loop; var block = loop.body; @@ -31,7 +31,7 @@ exports.ForOfStatement = function (node, parent, file, scope) { return loop; }; -var loose = function (node, parent, file, scope) { +var loose = function (node, parent, scope, context, file) { var left = node.left; var declar, id; @@ -63,7 +63,7 @@ var loose = function (node, parent, file, scope) { }; }; -var spec = function (node, parent, file, scope) { +var spec = function (node, parent, scope, context, file) { var left = node.left; var declar; diff --git a/lib/6to5/transformation/transformers/es6-let-scoping.js b/lib/6to5/transformation/transformers/es6-let-scoping.js index bdce7be036..7c589851cd 100644 --- a/lib/6to5/transformation/transformers/es6-let-scoping.js +++ b/lib/6to5/transformation/transformers/es6-let-scoping.js @@ -34,7 +34,7 @@ exports.VariableDeclaration = function (node, parent) { isLet(node, parent); }; -exports.Loop = function (node, parent, file, scope) { +exports.Loop = function (node, parent, scope, context, file) { var init = node.left || node.init; if (isLet(init, node)) { t.ensureBlock(node); @@ -46,7 +46,7 @@ exports.Loop = function (node, parent, file, scope) { node.label = parent.label; } - var letScoping = new LetScoping(node, node.body, parent, file, scope); + var letScoping = new LetScoping(node, node.body, parent, scope, file); letScoping.run(); if (node.label && !t.isLabeledStatement(parent)) { @@ -55,9 +55,9 @@ exports.Loop = function (node, parent, file, scope) { } }; -exports.BlockStatement = function (block, parent, file, scope) { +exports.BlockStatement = function (block, parent, scope, context, file) { if (!t.isLoop(parent)) { - var letScoping = new LetScoping(false, block, parent, file, scope); + var letScoping = new LetScoping(false, block, parent, scope, file); letScoping.run(); } }; @@ -68,11 +68,11 @@ exports.BlockStatement = function (block, parent, file, scope) { * @param {Boolean|Node} loopParent * @param {Node} block * @param {Node} parent - * @param {File} file * @param {Scope} scope + * @param {File} file */ -function LetScoping(loopParent, block, parent, file, scope) { +function LetScoping(loopParent, block, parent, scope, file) { this.loopParent = loopParent; this.parent = parent; this.scope = scope; @@ -167,7 +167,7 @@ LetScoping.prototype.remap = function () { if (!this.info.hasDuplicates) return; - var replace = function (node, parent, scope) { + var replace = function (node, parent, scope, context, replacements) { if (!t.isIdentifier(node)) return; if (!t.isReferenced(node, parent)) return; if (scope && scope.hasOwn(node.name)) return; @@ -176,7 +176,7 @@ LetScoping.prototype.remap = function () { var traverseReplace = function (node, parent) { replace(node, parent); - traverse(node, { enter: replace }); + traverse(node, { enter: replace }, null, replacements); }; var loopParent = this.loopParent; @@ -186,7 +186,7 @@ LetScoping.prototype.remap = function () { traverseReplace(loopParent.update, loopParent); } - traverse(block, { enter: replace }); + traverse(block, { enter: replace }, null, replacements); }; /** @@ -277,11 +277,11 @@ LetScoping.prototype.checkLoop = function () { }; traverse(this.block, { - enter: function (node, parent) { + enter: function (node, parent, scope, context) { var replace; if (t.isFunction(node) || t.isLoop(node)) { - return this.skip(); + return context.skip(); } if (node && !node.label) { @@ -305,7 +305,7 @@ LetScoping.prototype.checkLoop = function () { if (replace) return t.inherits(replace, node); } - }); + }, null, has); return has; }; @@ -316,9 +316,8 @@ LetScoping.prototype.checkLoop = function () { */ LetScoping.prototype.hoistVarDeclarations = function () { - var self = this; traverse(this.block, { - enter: function (node, parent) { + enter: function (node, parent, scope, context, self) { if (t.isForStatement(node)) { if (isVar(node.init, node)) { node.init = t.sequenceExpression(self.pushDeclar(node.init)); @@ -330,10 +329,10 @@ LetScoping.prototype.hoistVarDeclarations = function () { } else if (isVar(node, parent)) { return self.pushDeclar(node).map(t.expressionStatement); } else if (t.isFunction(node)) { - return this.skip(); + return context.skip(); } } - }); + }, null, this); }; /** @@ -359,13 +358,15 @@ LetScoping.prototype.getParams = function (params) { */ LetScoping.prototype.getLetReferences = function () { - var closurify = false; - var self = this; + var state = { + self: this, + closurify: false + }; // traverse through this block, stopping on functions and checking if they // contain any outside let references traverse(this.block, { - enter: function (node, parent, scope) { + enter: function (node, parent, scope, context, state) { if (t.isFunction(node)) { traverse(node, { enter: function (node, parent) { @@ -379,22 +380,22 @@ LetScoping.prototype.getLetReferences = function () { // to our let scope if (scope.hasOwn(node.name, true)) return; - closurify = true; + state.closurify = true; // this key doesn't appear just outside our scope - if (!_.contains(self.info.outsideKeys, node.name)) return; + if (!_.contains(state.self.info.outsideKeys, node.name)) return; // push this badboy - self.letReferences[node.name] = node; + state.self.letReferences[node.name] = node; } - }); + }, null, state); - return this.skip(); + return context.skip(); } } - }); + }, null, state); - return closurify; + return state.closurify; }; /** diff --git a/lib/6to5/transformation/transformers/es6-modules.js b/lib/6to5/transformation/transformers/es6-modules.js index 9d9631095b..a9b60473f2 100644 --- a/lib/6to5/transformation/transformers/es6-modules.js +++ b/lib/6to5/transformation/transformers/es6-modules.js @@ -6,7 +6,7 @@ exports.ast = { } }; -exports.ImportDeclaration = function (node, parent, file) { +exports.ImportDeclaration = function (node, parent, scope, context, file) { var nodes = []; if (node.specifiers.length) { @@ -26,7 +26,7 @@ exports.ImportDeclaration = function (node, parent, file) { return nodes; }; -exports.ExportDeclaration = function (node, parent, file) { +exports.ExportDeclaration = function (node, parent, scope, context, file) { var nodes = []; if (node.declaration) { diff --git a/lib/6to5/transformation/transformers/es6-property-method-assignment.js b/lib/6to5/transformation/transformers/es6-property-method-assignment.js index 30a54102a5..7237ef1c4f 100644 --- a/lib/6to5/transformation/transformers/es6-property-method-assignment.js +++ b/lib/6to5/transformation/transformers/es6-property-method-assignment.js @@ -2,7 +2,7 @@ var nameMethod = require("../helpers/name-method"); var util = require("../../util"); var t = require("../../types"); -exports.Property = function (node, parent, file, scope) { +exports.Property = function (node, parent, scope, context, file) { if (!node.method) return; node.method = false; diff --git a/lib/6to5/transformation/transformers/es6-rest-parameters.js b/lib/6to5/transformation/transformers/es6-rest-parameters.js index d0757690b4..c618394c51 100644 --- a/lib/6to5/transformation/transformers/es6-rest-parameters.js +++ b/lib/6to5/transformation/transformers/es6-rest-parameters.js @@ -1,11 +1,11 @@ var util = require("../../util"); var t = require("../../types"); -exports.Function = function (node, parent, file) { +exports.Function = function (node, parent, scope, context, file) { if (!node.rest) return; var rest = node.rest; - delete node.rest; + node.rest = null; t.ensureBlock(node); diff --git a/lib/6to5/transformation/transformers/es6-spread.js b/lib/6to5/transformation/transformers/es6-spread.js index 8547bfa550..4fa64b5e2b 100644 --- a/lib/6to5/transformation/transformers/es6-spread.js +++ b/lib/6to5/transformation/transformers/es6-spread.js @@ -40,7 +40,7 @@ var build = function (props, file) { return nodes; }; -exports.ArrayExpression = function (node, parent, file) { +exports.ArrayExpression = function (node, parent, scope, context, file) { var elements = node.elements; if (!hasSpread(elements)) return; @@ -55,7 +55,7 @@ exports.ArrayExpression = function (node, parent, file) { return t.callExpression(t.memberExpression(first, t.identifier("concat")), nodes); }; -exports.CallExpression = function (node, parent, file, scope) { +exports.CallExpression = function (node, parent, scope, context, file) { var args = node.arguments; if (!hasSpread(args)) return; @@ -95,7 +95,7 @@ exports.CallExpression = function (node, parent, file, scope) { node.arguments.unshift(contextLiteral); }; -exports.NewExpression = function (node, parent, file) { +exports.NewExpression = function (node, parent, scope, context, file) { var args = node.arguments; if (!hasSpread(args)) return; diff --git a/lib/6to5/transformation/transformers/es6-template-literals.js b/lib/6to5/transformation/transformers/es6-template-literals.js index 8ad54914f7..d2f360b104 100644 --- a/lib/6to5/transformation/transformers/es6-template-literals.js +++ b/lib/6to5/transformation/transformers/es6-template-literals.js @@ -4,7 +4,7 @@ var buildBinaryExpression = function (left, right) { return t.binaryExpression("+", left, right); }; -exports.TaggedTemplateExpression = function (node, parent, file) { +exports.TaggedTemplateExpression = function (node, parent, scope, context, file) { var args = []; var quasi = node.quasi; diff --git a/lib/6to5/transformation/transformers/es7-abstract-references.js b/lib/6to5/transformation/transformers/es7-abstract-references.js index 89bc7c05e4..39003b914c 100644 --- a/lib/6to5/transformation/transformers/es7-abstract-references.js +++ b/lib/6to5/transformation/transformers/es7-abstract-references.js @@ -21,7 +21,7 @@ var container = function (parent, call, ret) { } }; -exports.AssignmentExpression = function (node, parent, file, scope) { +exports.AssignmentExpression = function (node, parent, scope, context, file) { var left = node.left; if (!t.isVirtualPropertyExpression(left)) return; @@ -74,7 +74,7 @@ exports.UnaryExpression = function (node, parent) { return container(parent, call, t.literal(true)); }; -exports.CallExpression = function (node, parent, file, scope) { +exports.CallExpression = function (node, parent, scope, context, file) { var callee = node.callee; if (!t.isVirtualPropertyExpression(callee)) return; diff --git a/lib/6to5/transformation/transformers/es7-array-comprehension.js b/lib/6to5/transformation/transformers/es7-array-comprehension.js index 0a18a94e8a..e50b135d0b 100644 --- a/lib/6to5/transformation/transformers/es7-array-comprehension.js +++ b/lib/6to5/transformation/transformers/es7-array-comprehension.js @@ -5,7 +5,7 @@ var t = require("../../types"); exports.experimental = true; -var build = function (node, parent, file, scope) { +var build = function (node, parent, scope, context, file) { var uid = scope.generateUidBasedOnNode(parent, file); var container = util.template("array-comprehension-container", { @@ -34,8 +34,8 @@ var build = function (node, parent, file, scope) { return container; }; -exports.ComprehensionExpression = function (node, parent, file, scope) { +exports.ComprehensionExpression = function (node, parent, scope, context, file) { if (node.generator) return; - return build(node, parent, file, scope); + return build(node, parent, scope, context, file); }; diff --git a/lib/6to5/transformation/transformers/es7-object-spread.js b/lib/6to5/transformation/transformers/es7-object-spread.js index a9ffecaf03..083bbdc166 100644 --- a/lib/6to5/transformation/transformers/es7-object-spread.js +++ b/lib/6to5/transformation/transformers/es7-object-spread.js @@ -4,7 +4,7 @@ var t = require("../../types"); exports.experimental = true; -exports.ObjectExpression = function (node, parent, file) { +exports.ObjectExpression = function (node, parent, scope, context, file) { var hasSpread = false; var i; var prop; diff --git a/lib/6to5/transformation/transformers/optional-async-to-generator.js b/lib/6to5/transformation/transformers/optional-async-to-generator.js index 143445a9c2..d6f952783c 100644 --- a/lib/6to5/transformation/transformers/optional-async-to-generator.js +++ b/lib/6to5/transformation/transformers/optional-async-to-generator.js @@ -5,7 +5,7 @@ exports.optional = true; exports.manipulateOptions = bluebirdCoroutines.manipulateOptions; -exports.Function = function (node, parent, file) { +exports.Function = function (node, parent, scope, context, file) { if (!node.async || node.generator) return; return remapAsyncToGenerator(node, file.addHelper("async-to-generator")); diff --git a/lib/6to5/transformation/transformers/optional-bluebird-coroutines.js b/lib/6to5/transformation/transformers/optional-bluebird-coroutines.js index 5338fa1e1c..73b9db1fd7 100644 --- a/lib/6to5/transformation/transformers/optional-bluebird-coroutines.js +++ b/lib/6to5/transformation/transformers/optional-bluebird-coroutines.js @@ -9,7 +9,7 @@ exports.manipulateOptions = function (opts) { exports.optional = true; -exports.Function = function (node, parent, file) { +exports.Function = function (node, parent, scope, context, file) { if (!node.async || node.generator) return; return remapAsyncToGenerator( diff --git a/lib/6to5/transformation/transformers/optional-core-aliasing.js b/lib/6to5/transformation/transformers/optional-core-aliasing.js index 3610ae8f62..d563bb619a 100644 --- a/lib/6to5/transformation/transformers/optional-core-aliasing.js +++ b/lib/6to5/transformation/transformers/optional-core-aliasing.js @@ -26,7 +26,7 @@ exports.ast = { exit: function (ast, file) { traverse(ast, { - enter: function (node, parent) { + enter: function (node, parent, scope, context) { var prop; if (t.isMemberExpression(node) && t.isReferenced(node, parent)) { @@ -37,7 +37,7 @@ exports.ast = { if (!t.isReferenced(obj, node)) return; if (!node.computed && coreHas(obj) && _.has(core[obj.name], prop.name)) { - this.skip(); + context.skip(); return t.prependToMemberExpression(node, file._coreId); } } else if (t.isIdentifier(node) && !t.isMemberExpression(parent) && t.isReferenced(node, parent) && _.contains(ALIASABLE_CONSTRUCTORS, node.name)) { diff --git a/lib/6to5/transformation/transformers/optional-proto-to-assign.js b/lib/6to5/transformation/transformers/optional-proto-to-assign.js index e3f0a78022..73a9c8167d 100644 --- a/lib/6to5/transformation/transformers/optional-proto-to-assign.js +++ b/lib/6to5/transformation/transformers/optional-proto-to-assign.js @@ -17,7 +17,7 @@ var buildDefaultsCallExpression = function (expr, ref, file) { exports.optional = true; exports.secondPass = true; -exports.AssignmentExpression = function (node, parent, file, scope) { +exports.AssignmentExpression = function (node, parent, scope, context, file) { if (t.isExpressionStatement(parent)) return; if (!isProtoAssignmentExpression(node)) return; @@ -32,7 +32,7 @@ exports.AssignmentExpression = function (node, parent, file, scope) { return t.toSequenceExpression(nodes); }; -exports.ExpressionStatement = function (node, parent, file) { +exports.ExpressionStatement = function (node, parent, scope, context, file) { var expr = node.expression; if (!t.isAssignmentExpression(expr, { operator: "=" })) return; @@ -41,7 +41,7 @@ exports.ExpressionStatement = function (node, parent, file) { } }; -exports.ObjectExpression = function (node, parent, file) { +exports.ObjectExpression = function (node, parent, scope, context, file) { var proto; for (var i = 0; i < node.properties.length; i++) { diff --git a/lib/6to5/transformation/transformers/optional-typeof-symbol.js b/lib/6to5/transformation/transformers/optional-typeof-symbol.js index 36b8458936..1c1aed8cc1 100644 --- a/lib/6to5/transformation/transformers/optional-typeof-symbol.js +++ b/lib/6to5/transformation/transformers/optional-typeof-symbol.js @@ -2,8 +2,8 @@ var t = require("../../types"); exports.optional = true; -exports.UnaryExpression = function (node, parent, file) { - this.skip(); +exports.UnaryExpression = function (node, parent, scope, context, file) { + context.skip(); if (node.operator === "typeof") { var call = t.callExpression(file.addHelper("typeof"), [node.argument]); diff --git a/lib/6to5/transformation/transformers/playground-memoization-operator.js b/lib/6to5/transformation/transformers/playground-memoization-operator.js index 1f5fa4bb9f..1bd6d004ee 100644 --- a/lib/6to5/transformation/transformers/playground-memoization-operator.js +++ b/lib/6to5/transformation/transformers/playground-memoization-operator.js @@ -48,7 +48,7 @@ var buildAssignment = function (expr, obj, prop) { return t.assignmentExpression("=", buildAbsoluteRef(expr.left, obj, prop), expr.right); }; -exports.ExpressionStatement = function (node, parent, file, scope) { +exports.ExpressionStatement = function (node, parent, scope, context, file) { var expr = node.expression; if (!isMemo(expr)) return; @@ -66,7 +66,7 @@ exports.ExpressionStatement = function (node, parent, file, scope) { return nodes; }; -exports.AssignmentExpression = function (node, parent, file, scope) { +exports.AssignmentExpression = function (node, parent, scope, context, file) { if (t.isExpressionStatement(parent)) return; if (!isMemo(node)) return; diff --git a/lib/6to5/transformation/transformers/playground-method-binding.js b/lib/6to5/transformation/transformers/playground-method-binding.js index 6c6bd9e37b..4f3fe3bb51 100644 --- a/lib/6to5/transformation/transformers/playground-method-binding.js +++ b/lib/6to5/transformation/transformers/playground-method-binding.js @@ -1,6 +1,6 @@ var t = require("../../types"); -exports.BindMemberExpression = function (node, parent, file, scope) { +exports.BindMemberExpression = function (node, parent, scope, context, file) { var object = node.object; var prop = node.property; @@ -22,7 +22,7 @@ exports.BindMemberExpression = function (node, parent, file, scope) { } }; -exports.BindFunctionExpression = function (node, parent, file, scope) { +exports.BindFunctionExpression = function (node, parent, scope, context, file) { var buildCall = function (args) { var param = file.generateUidIdentifier("val", scope); return t.functionExpression(null, [param], t.blockStatement([ diff --git a/lib/6to5/transformation/transformers/playground-object-getter-memoization.js b/lib/6to5/transformation/transformers/playground-object-getter-memoization.js index 9c3f0bb6d0..08af63ea57 100644 --- a/lib/6to5/transformation/transformers/playground-object-getter-memoization.js +++ b/lib/6to5/transformation/transformers/playground-object-getter-memoization.js @@ -2,7 +2,7 @@ var traverse = require("../../traverse"); var t = require("../../types"); exports.Property = -exports.MethodDefinition = function (node, parent, file) { +exports.MethodDefinition = function (node, parent, scope, context, file) { if (node.kind !== "memo") return; node.kind = "get"; diff --git a/lib/6to5/transformation/transformers/react.js b/lib/6to5/transformation/transformers/react.js index 4dc53db287..76592d97c2 100644 --- a/lib/6to5/transformation/transformers/react.js +++ b/lib/6to5/transformation/transformers/react.js @@ -15,7 +15,7 @@ exports.XJSIdentifier = function (node) { } }; -exports.XJSNamespacedName = function (node, parent, file) { +exports.XJSNamespacedName = function (node, parent, scope, context, file) { throw file.errorWithNode(node, "Namespace tags are not supported. ReactJSX is not XML."); }; @@ -42,7 +42,7 @@ var isCompatTag = function(tagName) { }; exports.XJSOpeningElement = { - exit: function (node, parent, file) { + exit: function (node, parent, scope, context, file) { var reactCompat = file.opts.reactCompat; var tagExpr = node.name; var args = []; diff --git a/lib/6to5/transformation/transformers/spec-no-for-in-of-assignment.js b/lib/6to5/transformation/transformers/spec-no-for-in-of-assignment.js index 5af12108f1..0b00e88f9d 100644 --- a/lib/6to5/transformation/transformers/spec-no-for-in-of-assignment.js +++ b/lib/6to5/transformation/transformers/spec-no-for-in-of-assignment.js @@ -1,7 +1,7 @@ var t = require("../../types"); exports.ForInStatement = -exports.ForOfStatement = function (node, parent, file) { +exports.ForOfStatement = function (node, parent, scope, context, file) { var left = node.left; if (t.isVariableDeclaration(left)) { var declar = left.declarations[0]; diff --git a/lib/6to5/transformation/transformers/spec-setters.js b/lib/6to5/transformation/transformers/spec-setters.js index 30b404ed1e..54d88827aa 100644 --- a/lib/6to5/transformation/transformers/spec-setters.js +++ b/lib/6to5/transformation/transformers/spec-setters.js @@ -1,5 +1,5 @@ exports.MethodDefinition = -exports.Property = function (node, parent, file) { +exports.Property = function (node, parent, scope, context, file) { if (node.kind === "set" && node.value.params.length !== 1) { throw file.errorWithNode(node.value, "Setters must have only one parameter"); } diff --git a/lib/6to5/traverse/index.js b/lib/6to5/traverse/index.js index 5667ce1f46..7307a7665d 100644 --- a/lib/6to5/traverse/index.js +++ b/lib/6to5/traverse/index.js @@ -1,14 +1,18 @@ module.exports = traverse; +/* jshint maxparams:7 */ + var Scope = require("./scope"); var t = require("../types"); var _ = require("lodash"); -function TraversalContext(previousContext) { +function noop() { } + +function TraversalContext() { this.didSkip = false; this.didRemove = false; this.didStop = false; - this.didFlatten = previousContext ? previousContext.didFlatten : false; + this.didFlatten = false; } TraversalContext.prototype.flatten = function () { @@ -17,7 +21,7 @@ TraversalContext.prototype.flatten = function () { TraversalContext.prototype.remove = function () { this.didRemove = true; - this.skip(); + this.didSkip = true; }; TraversalContext.prototype.skip = function () { @@ -26,13 +30,16 @@ TraversalContext.prototype.skip = function () { TraversalContext.prototype.stop = function () { this.didStop = true; - this.skip(); + this.didSkip = true; }; -TraversalContext.prototype.maybeReplace = function (result, obj, key, node) { - if (result === false) return node; - if (result == null) return node; +TraversalContext.prototype.reset = function () { + this.didSkip = false; + this.didStop = false; + this.didRemove = false; +}; +function replaceNode(obj, key, node, result) { var isArray = Array.isArray(result); // inherit comments from original node to the first replacement node @@ -41,7 +48,7 @@ TraversalContext.prototype.maybeReplace = function (result, obj, key, node) { if (inheritTo) t.inheritsComments(inheritTo, node); // replace the node - node = obj[key] = result; + obj[key] = result; // we're replacing a statement or block node with an array of statements so we better // ensure that it's a block @@ -50,104 +57,139 @@ TraversalContext.prototype.maybeReplace = function (result, obj, key, node) { } if (isArray) { - this.flatten(); + return true; + } +} + +TraversalContext.prototype.enterNode = function (obj, key, node, enter, parent, scope, state) { + var result = enter(node, parent, scope, this, state); + var flatten = false; + + if (result) { + flatten = replaceNode(obj, key, node, result); + node = result; + + if (flatten) { + this.didFlatten = true; + } + } + + if (this.didRemove) { + obj[key] = null; + this.didFlatten = true; } return node; }; -TraversalContext.prototype.visit = function (obj, key, opts, scope, parent) { - var node = obj[key]; - if (!node) return; +TraversalContext.prototype.exitNode = function (obj, key, node, exit, parent, scope, state) { + var result = exit(node, parent, scope, this, state); + var flatten = false; - // type is blacklisted - if (opts.blacklist && opts.blacklist.indexOf(node.type) > -1) return; + if (result) { + flatten = replaceNode(obj, key, node, result); + node = result; - var result; - var ourScope = scope; - if (t.isScope(node)) ourScope = new Scope(node, scope); - - // enter - if (opts.enter) { - result = opts.enter.call(this, node, parent, ourScope); - node = this.maybeReplace(result, obj, key, node); - - if (this.didRemove) { - obj[key] = null; - this.flatten(); + if (flatten) { + this.didFlatten = true; } - - // stop traversal - if (this.didSkip) return; } - // traverse node - traverse(node, opts, ourScope); - - // exit - if (opts.exit) { - result = opts.exit.call(this, node, parent, ourScope); - node = this.maybeReplace(result, obj, key, node); - } + return node; }; -function traverse(parent, opts, scope) { - // falsy node - if (!parent) return; +TraversalContext.prototype.visitNode = function (obj, key, opts, scope, parent, state) { + this.reset(); - // array of nodes - if (Array.isArray(parent)) { - for (var i = 0; i < parent.length; i++) - traverse(parent[i], opts, scope); + var node = obj[key]; + + // type is blacklisted + if (opts.blacklist && opts.blacklist.indexOf(node.type) > -1) + return; + + var ourScope = scope; + if (t.isScope(node)) + ourScope = new Scope(node, scope); + + node = this.enterNode(obj, key, node, opts.enter, parent, ourScope, state); + + if (this.didSkip) + return this.didStop; + + traverseNode(node, opts, ourScope, state); + this.exitNode(obj, key, node, opts.exit, parent, ourScope, state); + + return this.didStop; +}; + +TraversalContext.prototype.visit = function (node, key, opts, scope, state) { + var nodes = node[key]; + if (!nodes) return; + + if (!Array.isArray(nodes)) { + return this.visitNode(node, key, opts, scope, node, state); + } + + if (nodes.length === 0) { return; } - // unknown node type to traverse - var keys = t.VISITOR_KEYS[parent.type]; + for (var k = 0; k < nodes.length; k++) { + if (nodes[k] && this.visitNode(nodes, k, opts, scope, node, state)) + return true; + } + + if (this.didFlatten) { + node[key] = _.flatten(node[key]); + + if (key === "body") { + // we can safely compact this + node[key] = _.compact(node[key]); + } + } +}; + +function traverseNode(node, opts, scope, state) { + var keys = t.VISITOR_KEYS[node.type]; if (!keys) return; - opts = opts || {}; - var context = null; - + var context = new TraversalContext(); for (var j = 0; j < keys.length; j++) { - var key = keys[j]; - var nodes = parent[key]; - if (!nodes) continue; + if (context.visit(node, keys[j], opts, scope, state)) + return; + } +} - if (Array.isArray(nodes)) { - for (var k = 0; k < nodes.length; k++) { - context = new TraversalContext(context); - context.visit(nodes, k, opts, scope, parent); - if (context.didStop) return; - } +function traverse(parent, opts, scope, state) { + // falsy node + if (!parent) return; - if (context && context.didFlatten) { - parent[key] = _.flatten(parent[key]); + if (!opts) opts = {}; + if (!opts.enter) opts.enter = noop; + if (!opts.exit) opts.exit = noop; - if (key === "body") { - // we can safely compact this - parent[key] = _.compact(parent[key]); - } - } - } else { - context = new TraversalContext(context); - context.visit(parent, key, opts, scope, parent); - if (context.didStop) return; - } + // array of nodes + if (!Array.isArray(parent)) { + traverseNode(parent, opts, scope, state); + return; + } + + for (var i = 0; i < parent.length; i++) { + traverseNode(parent[i], opts, scope, state); } } traverse.removeProperties = function (tree) { var clear = function (node) { - delete node._declarations; - delete node.extendedRange; - delete node._scopeInfo; - delete node.tokens; - delete node.range; - delete node.start; - delete node.end; - delete node.loc; - delete node.raw; + node._declarations = null; + node.extendedRange = null; + node._scopeInfo = null; + node.tokens = null; + node.range = null; + node.start = null; + node.end = null; + node.loc = null; + node.raw = null; clearComments(node.trailingComments); clearComments(node.leadingComments); @@ -166,23 +208,25 @@ traverse.removeProperties = function (tree) { traverse.hasType = function (tree, type, blacklistTypes) { blacklistTypes = [].concat(blacklistTypes || []); - var has = false; - // the node we're searching in is blacklisted if (_.contains(blacklistTypes, tree.type)) return false; // the type we're looking for is the same as the passed node if (tree.type === type) return true; + var state = { + has: false + }; + traverse(tree, { blacklist: blacklistTypes, - enter: function (node) { + enter: function (node, parent, scope, context, state) { if (node.type === type) { - has = true; - this.skip(); + state.has = true; + context.skip(); } } - }); + }, null, state); - return has; + return state.has; }; diff --git a/lib/6to5/traverse/scope.js b/lib/6to5/traverse/scope.js index f02803c886..320728c059 100644 --- a/lib/6to5/traverse/scope.js +++ b/lib/6to5/traverse/scope.js @@ -159,8 +159,13 @@ Scope.prototype.getInfo = function () { // Program, Function - var variables if (t.isProgram(block) || t.isFunction(block)) { + var state = { + blockId: block.id, + add: add + }; + traverse(block, { - enter: function (node, parent, scope) { + enter: function (node, parent, scope, context, state) { if (t.isFor(node)) { _.each(FOR_KEYS, function (key) { var declar = node[key]; @@ -170,22 +175,22 @@ Scope.prototype.getInfo = function () { // this block is a function so we'll stop since none of the variables // declared within are accessible - if (t.isFunction(node)) return this.skip(); + if (t.isFunction(node)) return context.skip(); // function identifier doesn't belong to this scope - if (block.id && node === block.id) return; + if (state.blockId && node === state.blockId) return; if (t.isIdentifier(node) && t.isReferenced(node, parent) && !scope.has(node.name)) { - add(node, true); + state.add(node, true); } // we've ran into a declaration! // we'll let the BlockStatement scope deal with `let` declarations unless if (t.isDeclaration(node) && !t.isLet(node)) { - add(node); + state.add(node); } } - }, this); + }, this, state); } // Function - params, rest diff --git a/lib/6to5/types/index.js b/lib/6to5/types/index.js index 2891d2df82..9155d41bd4 100644 --- a/lib/6to5/types/index.js +++ b/lib/6to5/types/index.js @@ -1,5 +1,6 @@ -var esutils = require("esutils"); -var _ = require("lodash"); +var esutils = require("esutils"); +var _ = require("lodash"); +var toFastProperties = require("../to-fast-properties"); var t = exports; @@ -573,3 +574,6 @@ t.getSpecifierName = function (specifier) { t.isSpecifierDefault = function (specifier) { return t.isIdentifier(specifier.id) && specifier.id.name === "default"; }; + +toFastProperties(t); +toFastProperties(t.VISITOR_KEYS); \ No newline at end of file diff --git a/lib/6to5/util.js b/lib/6to5/util.js index 66af587e4f..626d06305d 100644 --- a/lib/6to5/util.js +++ b/lib/6to5/util.js @@ -129,6 +129,14 @@ exports.buildDefineProperties = function (mutatorMap) { return objExpr; }; +var templateTraverser = { + enter: function (node, parent, scope, context, nodes) { + if (t.isIdentifier(node) && _.has(nodes, node.name)) { + return nodes[node.name]; + } + } +}; + exports.template = function (name, nodes, keepExpression) { var template = exports.templates[name]; if (!template) throw new ReferenceError("unknown template " + name); @@ -141,13 +149,7 @@ exports.template = function (name, nodes, keepExpression) { template = _.cloneDeep(template); if (!_.isEmpty(nodes)) { - traverse(template, { - enter: function (node) { - if (t.isIdentifier(node) && _.has(nodes, node.name)) { - return nodes[node.name]; - } - } - }); + traverse(template, templateTraverser, null, nodes); } var node = template.body[0];