From 49e7e3b9989ea194fccdb256258aa7df5f022227 Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Mon, 24 Nov 2014 00:35:18 +1100 Subject: [PATCH] fix multiple references in experimental abstract references #207 --- CHANGELOG.md | 4 ++ lib/6to5/file.js | 18 +++--- .../transformers/_declarations.js | 15 +++-- .../transformers/es7-abstract-references.js | 58 ++++++++++++++++--- lib/6to5/traverse/index.js | 1 + lib/6to5/traverse/scope.js | 19 ++++++ 6 files changed, 89 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b6b5ac04c1..709642f1bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 1.13.8 + + * Only use a single reference in abstract references. + # 1.13.7 * Upgrade `acorn-6to5`. diff --git a/lib/6to5/file.js b/lib/6to5/file.js index 600024323d..8a6a5a1c97 100644 --- a/lib/6to5/file.js +++ b/lib/6to5/file.js @@ -13,9 +13,8 @@ function File(opts) { this.opts = File.normaliseOptions(opts); this.moduleFormatter = this.getModuleFormatter(this.opts.modules); - this.declarations = {}; - this.uids = {}; - this.ast = {}; + this.uids = {}; + this.ast = {}; } File.declarations = [ @@ -123,8 +122,10 @@ File.prototype.addDeclaration = function (name) { throw new ReferenceError("unknown declaration " + name); } - var declar = this.declarations[name]; - if (declar) return declar.uid; + var program = this.ast.program; + + var declar = program._declarations && program._declarations[name]; + if (declar) return declar.id; var ref; var runtimeNamespace = this.opts.runtime; @@ -135,11 +136,8 @@ File.prototype.addDeclaration = function (name) { ref = util.template(name); } - var uid = t.identifier(this.generateUid(name)); - this.declarations[name] = { - uid: uid, - node: ref - }; + var uid = this.generateUidIdentifier(name); + this.scope.push(name, uid, ref); return uid; }; diff --git a/lib/6to5/transformation/transformers/_declarations.js b/lib/6to5/transformation/transformers/_declarations.js index fc3429c626..88d4444564 100644 --- a/lib/6to5/transformation/transformers/_declarations.js +++ b/lib/6to5/transformation/transformers/_declarations.js @@ -1,12 +1,11 @@ var t = require("../../types"); +var _ = require("lodash"); -module.exports = function (ast, file) { - var body = ast.program.body; - - for (var i in file.declarations) { - var declar = file.declarations[i]; - body.unshift(t.variableDeclaration("var", [ - t.variableDeclarator(declar.uid, declar.node) +exports.BlockStatement = +exports.Program = function (node, parent, file) { + _.each(node._declarations, function (declar) { + node.body.unshift(t.variableDeclaration("var", [ + t.variableDeclarator(declar.id, declar.init) ])); - } + }); }; diff --git a/lib/6to5/transformation/transformers/es7-abstract-references.js b/lib/6to5/transformation/transformers/es7-abstract-references.js index c17d9924d2..7a7ff7d7f5 100644 --- a/lib/6to5/transformation/transformers/es7-abstract-references.js +++ b/lib/6to5/transformation/transformers/es7-abstract-references.js @@ -8,23 +8,48 @@ var container = function (parent, call, ret) { // we don't need to worry about return values return call; } else { - return t.sequenceExpression([call, ret]); + var exprs = []; + if (t.isSequenceExpression(call)) { + exprs = call.expressions; + } else { + exprs.push(call); + } + exprs.push(ret); + return t.sequenceExpression(exprs); } }; -exports.AssignmentExpression = function (node, parent) { +exports.AssignmentExpression = function (node, parent, file, scope) { var left = node.left; if (!t.isVirtualPropertyExpression(left)) return; - var right = node.right; + var value = node.right; + var temp; + + // we need to return `node.right` + if (!t.isExpressionStatement(parent)) { + // `node.right` isn't a simple identifier so we need to reference it + if (!t.isIdentifier(value)) { + var tempName = file.generateUid("temp"); + temp = value = t.identifier(tempName); + scope.push(tempName, temp); + } + } var call = util.template("abstract-expression-set", { PROPERTY: left.property, OBJECT: left.object, - VALUE: right + VALUE: value }); - return container(parent, call, right); + if (temp) { + call = t.sequenceExpression([ + t.assignmentExpression("=", temp, node.right), + call + ]); + } + + return container(parent, call, value); }; exports.UnaryExpression = function (node, parent) { @@ -40,16 +65,33 @@ exports.UnaryExpression = function (node, parent) { return container(parent, call, t.literal(true)); }; -exports.CallExpression = function (node) { +exports.CallExpression = function (node, parent, file, scope) { var callee = node.callee; if (!t.isVirtualPropertyExpression(callee)) return; + var temp; + if (!t.isIdentifier(callee.object)) { + // we need to save `callee.object` so we can call it again + var tempName = file.generateUid("temp"); + temp = t.identifier(tempName); + scope.push(tempName, temp); + } + var call = util.template("abstract-expression-call", { PROPERTY: callee.property, - OBJECT: callee.object + OBJECT: temp || callee.object }); + call.arguments = call.arguments.concat(node.arguments); - return call; + + if (temp) { + return t.sequenceExpression([ + t.assignmentExpression("=", temp, callee.object), + call + ]); + } else { + return call; + } }; exports.VirtualPropertyExpression = function (node) { diff --git a/lib/6to5/traverse/index.js b/lib/6to5/traverse/index.js index 5e56d891c4..227bee3bd5 100644 --- a/lib/6to5/traverse/index.js +++ b/lib/6to5/traverse/index.js @@ -90,6 +90,7 @@ function traverse(parent, callbacks, opts) { traverse.removeProperties = function (tree) { var clear = function (node) { delete node._scopeReferences; + delete node._declarations; delete node.extendedRange; delete node._parent; delete node._scope; diff --git a/lib/6to5/traverse/scope.js b/lib/6to5/traverse/scope.js index fa6e5b52e4..1747f29d44 100644 --- a/lib/6to5/traverse/scope.js +++ b/lib/6to5/traverse/scope.js @@ -97,6 +97,25 @@ Scope.prototype.getReferences = function () { return references; }; +Scope.prototype.push = function (name, id, init) { + var block = this.block; + + if (t.isFor(block) || t.isCatchClause(block) || t.isFunction(block)) { + t.ensureBlock(block); + block = block.body; + } + + if (t.isBlockStatement(block) || t.isProgram(block)) { + block._declarations = block._declarations || {}; + block._declarations[name] = { + id: id, + init: init + }; + } else { + throw new Error("wtf"); + } +}; + Scope.prototype.add = function (node, references) { if (!node) return; _.merge(references || this.references, t.getIds(node, true));