From f373f8f0035d1af31f18e6b1cbecafdbedbf5e7a Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Mon, 11 May 2015 11:38:22 +0100 Subject: [PATCH] refactor _shadowFunctions transformer to not do an entire traverse per function --- .../transformers/internal/shadow-functions.js | 118 ++++-------------- .../transformers/other/strict.js | 4 +- src/babel/traversal/path/index.js | 21 +++- 3 files changed, 44 insertions(+), 99 deletions(-) diff --git a/src/babel/transformation/transformers/internal/shadow-functions.js b/src/babel/transformation/transformers/internal/shadow-functions.js index 92ab2f9687..493c2c3f1d 100644 --- a/src/babel/transformation/transformers/internal/shadow-functions.js +++ b/src/babel/transformation/transformers/internal/shadow-functions.js @@ -1,105 +1,33 @@ import * as t from "../../../types"; -var functionChildrenVisitor = { - enter(node, parent, scope, state) { - if (this.isClass(node)) { - return this.skip(); - } - - if (this.isFunction() && !node.shadow) { - return this.skip(); - } - - if (node._shadowedFunctionLiteral) return this.skip(); - - var getId; - - if (this.isIdentifier() && node.name === "arguments") { - getId = state.getArgumentsId; - } else if (this.isThisExpression()) { - getId = state.getThisId; - } else { - return; - } - - if (this.isReferenced()) return getId(); - } -}; - -var functionVisitor = { - enter(node, parent, scope, state) { - if (!node.shadow) { - if (this.isFunction()) { - // 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` - this.traverse(functionChildrenVisitor, state); - - node.shadow = false; - - return this.skip(); - } -}; - -function aliasFunction(getBody, path, scope) { - var argumentsId; - var thisId; - - var state = { - getArgumentsId() { - return argumentsId = argumentsId || scope.generateUidIdentifier("arguments"); - }, - - getThisId() { - return thisId = thisId || scope.generateUidIdentifier("this"); - } - }; - - // traverse the function and find all alias functions so we can alias - // `arguments` and `this` if necessary - path.traverse(functionVisitor, state); - - var body; - - var pushDeclaration = function (id, init) { - body = body || getBody(); - body.unshift(t.variableDeclaration("var", [ - t.variableDeclarator(id, init) - ])); - }; - - if (argumentsId) { - pushDeclaration(argumentsId, t.identifier("arguments")); - } - - if (thisId) { - pushDeclaration(thisId, t.thisExpression()); - } -}; - -// todo: on all `this` and `arguments`, walk UP the tree instead of -// crawling the entire function tree - export var metadata = { group: "builtin-trailing" }; -export function Program(node, parent, scope) { - aliasFunction(function () { - return node.body; - }, this, scope); +function remap(path, key, create) { + // ensure that we're shadowed + if (!path.inShadow()) return; + + var fnPath = path.findParent((node, path) => !node.shadow && (path.isFunction() || path.isProgram())); + + var cached = fnPath.getData(key); + if (cached) return cached; + + var init = create(); + var id = path.scope.generateUidIdentifier(key); + + fnPath.setData(key, id); + fnPath.scope.push({ id, init }); + + return id; } -export function FunctionDeclaration(node, parent, scope) { - aliasFunction(function () { - t.ensureBlock(node); - return node.body.body; - }, this, scope); +export function ThisExpression() { + return remap(this, "this", () => t.thisExpression()); } -export { FunctionDeclaration as FunctionExpression }; +export function ReferencedIdentifier(node) { + if (node.name === "arguments" && !node._shadowedFunctionLiteral) { + return remap(this, "arguments", () => t.identifier("arguments")); + } +} diff --git a/src/babel/transformation/transformers/other/strict.js b/src/babel/transformation/transformers/other/strict.js index b227250600..20e6275a28 100644 --- a/src/babel/transformation/transformers/other/strict.js +++ b/src/babel/transformation/transformers/other/strict.js @@ -23,9 +23,7 @@ export var Program = { } export function ThisExpression() { - if (!this.findParent(function (node) { - return !node.shadow && THIS_BREAK_KEYS.indexOf(node.type) >= 0; - })) { + if (!this.findParent((node) => !node.shadow && THIS_BREAK_KEYS.indexOf(node.type) >= 0)) { return t.identifier("undefined"); } } diff --git a/src/babel/traversal/path/index.js b/src/babel/traversal/path/index.js index ac219889c5..13e173398b 100644 --- a/src/babel/traversal/path/index.js +++ b/src/babel/traversal/path/index.js @@ -125,6 +125,25 @@ export default class TraversalPath { return false; } + /** + * Description + */ + + inShadow() { + var path = this; + while (path) { + if (path.isFunction()) { + if (path.node.shadow) { + return path; + } else { + return null; + } + } + path = path.parentPath; + } + return null; + } + /** * Check whether this node was a part of the original AST. */ @@ -148,7 +167,7 @@ export default class TraversalPath { findParent(callback) { var path = this; while (path) { - if (callback(path.node)) return path.node; + if (callback(path.node, path)) return path; path = path.parentPath; } return null;