From 70d068226a2fc37786ec11c65bb47802b623eb12 Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Sat, 21 Mar 2015 15:26:55 +1100 Subject: [PATCH] add ability to explode a list of statements when trying to replace an expression - damn this is powerful and allows some SUPER cool scenarios --- ...-binary-assignment-operator-transformer.js | 3 +- ...itional-assignment-operator-transformer.js | 2 +- .../transformation/helpers/replace-supers.js | 14 +-- .../transformers/es6/arrow-functions.js | 3 +- .../transformers/es6/block-scoping.js | 3 +- .../transformers/es6/classes.js | 2 +- .../transformers/es6/destructuring.js | 53 ++++++----- .../transformers/es6/object-super.js | 2 +- .../transformers/es6/parameters.default.js | 5 +- .../transformers/es6/parameters.rest.js | 2 +- .../transformers/es6/properties.computed.js | 6 +- .../transformation/transformers/es6/spread.js | 2 +- .../transformers/es7/comprehensions.js | 5 +- .../transformation/transformers/index.js | 16 ++-- .../transformers/internal/alias-functions.js | 18 ++-- .../transformers/spec/function-name.js | 1 + .../transformers/spec/proto-to-assign.js | 4 +- src/babel/traversal/path/index.js | 92 ++++++++++++++++++- src/babel/traversal/scope.js | 11 +-- src/babel/types/alias-keys.json | 3 +- src/babel/types/builder-keys.json | 7 ++ src/babel/types/converters.js | 31 +++++-- src/babel/types/index.js | 29 +++--- 23 files changed, 211 insertions(+), 103 deletions(-) diff --git a/src/babel/transformation/helpers/build-binary-assignment-operator-transformer.js b/src/babel/transformation/helpers/build-binary-assignment-operator-transformer.js index 87511d54ab..b09292b3f3 100644 --- a/src/babel/transformation/helpers/build-binary-assignment-operator-transformer.js +++ b/src/babel/transformation/helpers/build-binary-assignment-operator-transformer.js @@ -33,8 +33,7 @@ export default function (exports, opts) { var nodes = []; var exploded = explode(node.left, nodes, file, scope); nodes.push(buildAssignment(exploded.ref, opts.build(exploded.uid, node.right))); - - return t.toSequenceExpression(nodes, scope); + return nodes; }; exports.BinaryExpression = function (node) { diff --git a/src/babel/transformation/helpers/build-conditional-assignment-operator-transformer.js b/src/babel/transformation/helpers/build-conditional-assignment-operator-transformer.js index d245e13b89..1cae5a8c4a 100644 --- a/src/babel/transformation/helpers/build-conditional-assignment-operator-transformer.js +++ b/src/babel/transformation/helpers/build-conditional-assignment-operator-transformer.js @@ -40,6 +40,6 @@ export default function (exports, opts) { // todo: duplicate expression node nodes.push(exploded.ref); - return t.toSequenceExpression(nodes, scope); + return nodes; }; }; diff --git a/src/babel/transformation/helpers/replace-supers.js b/src/babel/transformation/helpers/replace-supers.js index 61368f9119..0539f84820 100644 --- a/src/babel/transformation/helpers/replace-supers.js +++ b/src/babel/transformation/helpers/replace-supers.js @@ -5,7 +5,7 @@ import * as t from "../../types"; function isIllegalBareSuper(node, parent) { - if (!t.isSuperExpression(node)) return false; + if (!t.isSuper(node)) return false; if (t.isMemberExpression(parent, { computed: false })) return false; if (t.isCallExpression(parent, { callee: node })) return false; return true; @@ -189,13 +189,13 @@ export default class ReplaceSupers { looseHandle(path: TraversalPath, getThisReference: Function) { var node = path.node; - if (path.isSuperExpression()) { + if (path.isSuper()) { this.hasSuper = true; return this.getLooseSuperProperty(node, path.parent); } else if (path.isCallExpression()) { var callee = node.callee; if (!t.isMemberExpression(callee)) return; - if (!t.isSuperExpression(callee.object)) return; + if (!t.isSuper(callee.object)) return; // super.test(); -> objectRef.prototype.MethodName.call(this); this.hasSuper = true; @@ -224,7 +224,7 @@ export default class ReplaceSupers { if (t.isCallExpression(node)) { var callee = node.callee; - if (t.isSuperExpression(callee)) { + if (t.isSuper(callee)) { // super(); -> _get(Object.getPrototypeOf(objectRef), "MethodName", this).call(this); property = methodNode.key; computed = methodNode.computed; @@ -237,17 +237,17 @@ export default class ReplaceSupers { var methodName = methodNode.key.name || "METHOD_NAME"; throw this.file.errorWithNode(node, messages.get("classesIllegalSuperCall", methodName)); } - } else if (t.isMemberExpression(callee) && t.isSuperExpression(callee.object)) { + } else if (t.isMemberExpression(callee) && t.isSuper(callee.object)) { // super.test(); -> _get(Object.getPrototypeOf(objectRef.prototype), "test", this).call(this); property = callee.property; computed = callee.computed; args = node.arguments; } - } else if (t.isMemberExpression(node) && t.isSuperExpression(node.object)) { + } else if (t.isMemberExpression(node) && t.isSuper(node.object)) { // super.name; -> _get(Object.getPrototypeOf(objectRef.prototype), "name", this); property = node.property; computed = node.computed; - } else if (t.isAssignmentExpression(node) && t.isSuperExpression(node.left.object) && methodNode.kind === "set") { + } else if (t.isAssignmentExpression(node) && t.isSuper(node.left.object) && methodNode.kind === "set") { // super.name = "val"; -> _set(Object.getPrototypeOf(objectRef.prototype), "name", this); this.hasSuper = true; return this.setSuperProperty(node.left.property, node.right, node.left.computed, getThisReference()); diff --git a/src/babel/transformation/transformers/es6/arrow-functions.js b/src/babel/transformation/transformers/es6/arrow-functions.js index 9751ee25f3..47d53a25fa 100644 --- a/src/babel/transformation/transformers/es6/arrow-functions.js +++ b/src/babel/transformation/transformers/es6/arrow-functions.js @@ -5,9 +5,8 @@ export var check = t.isArrowFunctionExpression; export function ArrowFunctionExpression(node) { t.ensureBlock(node); - node._aliasFunction = "arrow"; node.expression = false; - node.type = "FunctionExpression"; + node.type = "ShadowFunctionExpression"; return node; } diff --git a/src/babel/transformation/transformers/es6/block-scoping.js b/src/babel/transformation/transformers/es6/block-scoping.js index fe60790d4c..93ff7783bb 100644 --- a/src/babel/transformation/transformers/es6/block-scoping.js +++ b/src/babel/transformation/transformers/es6/block-scoping.js @@ -354,8 +354,7 @@ class BlockScoping { var params = values(outsideRefs); // build the closure that we're going to wrap the block with - var fn = t.functionExpression(null, params, t.blockStatement(block.body)); - fn._aliasFunction = true; + var fn = t.shadowFunctionExpression(null, params, t.blockStatement(block.body)); // replace the current block body with the one we're going to build block.body = this.body; diff --git a/src/babel/transformation/transformers/es6/classes.js b/src/babel/transformation/transformers/es6/classes.js index 5aa5e52ff9..e1e8e0bdb4 100644 --- a/src/babel/transformation/transformers/es6/classes.js +++ b/src/babel/transformation/transformers/es6/classes.js @@ -33,7 +33,7 @@ var verifyConstructorVisitor = traverse.explode({ CallExpression: { enter(node, parent, scope, state) { - if (this.get("callee").isSuperExpression()) { + if (this.get("callee").isSuper()) { state.hasBareSuper = true; if (!state.hasSuper) { diff --git a/src/babel/transformation/transformers/es6/destructuring.js b/src/babel/transformation/transformers/es6/destructuring.js index 9664315737..8c11eca2d5 100644 --- a/src/babel/transformation/transformers/es6/destructuring.js +++ b/src/babel/transformation/transformers/es6/destructuring.js @@ -113,22 +113,13 @@ export function ExpressionStatement(node, parent, scope, file) { if (!t.isPattern(expr.left)) return; if (file.isConsequenceExpressionStatement(node)) return; - var nodes = []; - - var ref = scope.generateUidIdentifier("ref"); - nodes.push(t.variableDeclaration("var", [ - t.variableDeclarator(ref, expr.right) - ])); - var destructuring = new DestructuringTransformer({ operator: expr.operator, - file: file, - scope: scope, - nodes: nodes + scope: scope, + file: file, }); - destructuring.init(expr.left, ref); - return nodes; + return destructuring.init(expr.left, expr.right); } export function AssignmentExpression(node, parent, scope, file) { @@ -138,7 +129,7 @@ export function AssignmentExpression(node, parent, scope, file) { scope.push({ id: ref }); var nodes = []; - nodes.push(t.assignmentExpression("=", ref, node.right)); + nodes.push(t.expressionStatement(t.assignmentExpression("=", ref, node.right))); var destructuring = new DestructuringTransformer({ operator: node.operator, @@ -148,9 +139,9 @@ export function AssignmentExpression(node, parent, scope, file) { }); destructuring.init(node.left, ref); - nodes.push(ref); + nodes.push(t.expressionStatement(ref)); - return t.toSequenceExpression(nodes, scope); + return nodes; } function variableDeclarationHasPattern(node) { @@ -218,20 +209,29 @@ export function VariableDeclaration(node, parent, scope, file) { return nodes; } -var hasRest = function (pattern) { +function hasRest(pattern) { for (var i = 0; i < pattern.elements.length; i++) { if (t.isRestElement(pattern.elements[i])) { return true; } } return false; +} + +var arrayUnpackVisitor = { + enter(node, parent, scope, state) { + if (this.isReferencedIdentifier() && state.bindings[node.name]) { + state.deopt = true; + this.stop(); + } + } }; class DestructuringTransformer { constructor(opts) { this.blockHoist = opts.blockHoist; this.operator = opts.operator; - this.nodes = opts.nodes; + this.nodes = opts.nodes || []; this.scope = opts.scope; this.file = opts.file; this.kind = opts.kind; @@ -393,7 +393,11 @@ class DestructuringTransformer { if (!pattern.elements[i]) return false; } - return true; + // deopt on reference to left side identifiers + var bindings = t.getBindingIdentifiers(pattern); + var state = { deopt: false, bindings }; + this.scope.traverse(arr, arrayUnpackVisitor, state); + return !state.deopt; } pushUnpackedArrayPattern(pattern, arr) { @@ -474,14 +478,19 @@ class DestructuringTransformer { // trying to destructure a value that we can't evaluate more than once so we // need to save it to a variable - if (!t.isArrayExpression(ref) && !t.isMemberExpression(ref) && !t.isIdentifier(ref)) { - var key = this.scope.generateUidBasedOnNode(ref); - this.nodes.push(this.buildVariableDeclaration(key, ref)); - ref = key; + var shouldMemoise = true; + if (!t.isArrayExpression(ref) && !t.isMemberExpression(ref)) { + var memo = this.scope.generateMemoisedReference(ref, true); + if (memo) { + this.nodes.push(this.buildVariableDeclaration(memo, ref)); + ref = memo; + } } // this.push(pattern, ref); + + return this.nodes; } } diff --git a/src/babel/transformation/transformers/es6/object-super.js b/src/babel/transformation/transformers/es6/object-super.js index f572df3006..842c5ed4e5 100644 --- a/src/babel/transformation/transformers/es6/object-super.js +++ b/src/babel/transformation/transformers/es6/object-super.js @@ -1,7 +1,7 @@ import ReplaceSupers from "../../helpers/replace-supers"; import * as t from "../../../types"; -export var check = t.isSuperExpression; +export var check = t.isSuper; function Property(node, scope, getObjectRef, file) { if (!node.method) return; diff --git a/src/babel/transformation/transformers/es6/parameters.default.js b/src/babel/transformation/transformers/es6/parameters.default.js index 70b3a1994c..7941967784 100644 --- a/src/babel/transformation/transformers/es6/parameters.default.js +++ b/src/babel/transformation/transformers/es6/parameters.default.js @@ -31,7 +31,7 @@ exports.Function = function (node, parent, scope, file) { var body = []; var argsIdentifier = t.identifier("arguments"); - argsIdentifier._ignoreAliasFunctions = true; + argsIdentifier._shadowedFunctionLiteral = true; var lastNonDefaultParam = 0; @@ -91,8 +91,7 @@ exports.Function = function (node, parent, scope, file) { node.params = node.params.slice(0, lastNonDefaultParam); if (state.iife) { - var container = t.functionExpression(null, [], node.body, node.generator); - container._aliasFunction = true; + var container = t.shadowFunctionExpression(null, [], node.body, node.generator); body.push(t.returnStatement(t.callExpression(container, []))); diff --git a/src/babel/transformation/transformers/es6/parameters.rest.js b/src/babel/transformation/transformers/es6/parameters.rest.js index d0f967a8f7..5122e6721a 100644 --- a/src/babel/transformation/transformers/es6/parameters.rest.js +++ b/src/babel/transformation/transformers/es6/parameters.rest.js @@ -63,7 +63,7 @@ exports.Function = function (node, parent, scope, file) { var argsId = t.identifier("arguments"); // otherwise `arguments` will be remapped in arrow functions - argsId._ignoreAliasFunctions = true; + argsId._shadowedFunctionLiteral = true; // support patterns if (t.isPattern(rest)) { diff --git a/src/babel/transformation/transformers/es6/properties.computed.js b/src/babel/transformation/transformers/es6/properties.computed.js index ca1f9ee715..9fef133b74 100644 --- a/src/babel/transformation/transformers/es6/properties.computed.js +++ b/src/babel/transformation/transformers/es6/properties.computed.js @@ -104,8 +104,6 @@ export function ObjectExpression(node, parent, scope, file) { // var body = []; - var container = t.functionExpression(null, [], t.blockStatement(body)); - container._aliasFunction = true; // @@ -121,7 +119,7 @@ export function ObjectExpression(node, parent, scope, file) { t.variableDeclarator(objId, t.objectExpression(initProps)) ])); - body.push(t.returnStatement(objId)); + body.push(t.expressionStatement(objId)); - return t.callExpression(container, []); + return body; } diff --git a/src/babel/transformation/transformers/es6/spread.js b/src/babel/transformation/transformers/es6/spread.js index 693553ded9..d83b4f957b 100644 --- a/src/babel/transformation/transformers/es6/spread.js +++ b/src/babel/transformation/transformers/es6/spread.js @@ -82,7 +82,7 @@ export function CallExpression(node, parent, scope) { var callee = node.callee; if (this.get("callee").isMemberExpression()) { - var temp = scope.generateTempBasedOnNode(callee.object); + var temp = scope.generateMemoisedReference(callee.object); if (temp) { callee.object = t.assignmentExpression("=", temp, callee.object); contextLiteral = temp; diff --git a/src/babel/transformation/transformers/es7/comprehensions.js b/src/babel/transformation/transformers/es7/comprehensions.js index 986f5eeb36..848a32ffe7 100644 --- a/src/babel/transformation/transformers/es7/comprehensions.js +++ b/src/babel/transformation/transformers/es7/comprehensions.js @@ -16,8 +16,7 @@ export function ComprehensionExpression(node, parent, scope, file) { function generator(node) { var body = []; - var container = t.functionExpression(null, [], t.blockStatement(body), true); - container._aliasFunction = true; + var container = t.shadowFunctionExpression(null, [], t.blockStatement(body), true); body.push(buildComprehension(node, function () { return t.expressionStatement(t.yieldExpression(node.body)); @@ -32,7 +31,7 @@ function array(node, parent, scope, file) { var container = util.template("array-comprehension-container", { KEY: uid }); - container.callee._aliasFunction = true; + container.callee.type = "ShadowFunctionExpression"; var block = container.callee.body; var body = block.body; diff --git a/src/babel/transformation/transformers/index.js b/src/babel/transformation/transformers/index.js index c2fd23be65..9ae788d1fa 100644 --- a/src/babel/transformation/transformers/index.js +++ b/src/babel/transformation/transformers/index.js @@ -8,9 +8,6 @@ export default { "validation.undeclaredVariableCheck": require("./validation/undeclared-variable-check"), "validation.react": require("./validation/react"), - // needs to be before `_aliasFunction` - "es6.arrowFunctions": require("./es6/arrow-functions"), - // this goes at the start so we only transform the original user code "spec.functionName": require("./spec/function-name"), @@ -22,7 +19,7 @@ export default { _modules: require("./internal/modules"), // needs to be before `regenerator` due to generator comprehensions - // needs to be before `_aliasFunction` + // needs to be before `_shadowFunctions` "es7.comprehensions": require("./es7/comprehensions"), "es6.classes": require("./es6/classes"), @@ -38,10 +35,10 @@ export default { "es5.properties.mutators": require("./es5/properties.mutators"), "es6.properties.shorthand": require("./es6/properties.shorthand"), - // needs to be before `_aliasFunction` due to define property closure + // needs to be before `_shadowFunctions` due to define property closure "es6.properties.computed": require("./es6/properties.computed"), - "optimisation.es6.forOf": require("./optimisation/flow.for-of"), + "optimisation.flow.forOf": require("./optimisation/flow.for-of"), "es6.forOf": require("./es6/for-of"), "es6.regex.sticky": require("./es6/regex.sticky"), @@ -61,7 +58,7 @@ export default { // needs to be before `es6.blockScoping` as let variables may be produced "es6.destructuring": require("./es6/destructuring"), - // needs to be before `_aliasFunction` due to block scopes sometimes being wrapped in a + // needs to be before `_shadowFunctions` due to block scopes sometimes being wrapped in a // closure "es6.blockScoping": require("./es6/block-scoping"), @@ -88,7 +85,10 @@ export default { _declarations: require("./internal/declarations"), - _aliasFunctions: require("./internal/alias-functions"), + // needs to be before `_shadowFunctions` + "es6.arrowFunctions": require("./es6/arrow-functions"), + + _shadowFunctions: require("./internal/alias-functions"), "es6.symbols": require("./es6/symbols"), "spec.undefinedToVoid": require("./spec/undefined-to-void"), diff --git a/src/babel/transformation/transformers/internal/alias-functions.js b/src/babel/transformation/transformers/internal/alias-functions.js index 649edd0c38..e92ece5a7f 100644 --- a/src/babel/transformation/transformers/internal/alias-functions.js +++ b/src/babel/transformation/transformers/internal/alias-functions.js @@ -2,11 +2,11 @@ import * as t from "../../../types"; var functionChildrenVisitor = { enter(node, parent, scope, state) { - if (this.isFunction() && !node._aliasFunction) { + if (this.isFunction()) { return this.skip(); } - if (node._ignoreAliasFunctions) return this.skip(); + if (node._shadowedFunctionLiteral) return this.skip(); var getId; @@ -24,18 +24,18 @@ var functionChildrenVisitor = { var functionVisitor = { enter(node, parent, scope, state) { - if (!node._aliasFunction) { - if (this.isFunction()) { - // stop traversal of this node as it'll be hit again by this transformer - return this.skip(); - } else { - return; - } + if (this.isFunction()) { + // stop traversal of this node as it'll be hit again by this transformer + return this.skip(); + } else if (!this.isShadowFunctionExpression()) { + return; } // traverse all child nodes of this function and find `arguments` and `this` this.traverse(functionChildrenVisitor, state); + node.type = "FunctionExpression"; + return this.skip(); } }; diff --git a/src/babel/transformation/transformers/spec/function-name.js b/src/babel/transformation/transformers/spec/function-name.js index 2d76beeef8..c04782ef0a 100644 --- a/src/babel/transformation/transformers/spec/function-name.js +++ b/src/babel/transformation/transformers/spec/function-name.js @@ -1 +1,2 @@ export { bare as FunctionExpression } from "../../helpers/name-method"; +export { bare as ArrowFunctionExpression } from "../../helpers/name-method"; diff --git a/src/babel/transformation/transformers/spec/proto-to-assign.js b/src/babel/transformation/transformers/spec/proto-to-assign.js index 6f6905784a..69fd3ad618 100644 --- a/src/babel/transformation/transformers/spec/proto-to-assign.js +++ b/src/babel/transformation/transformers/spec/proto-to-assign.js @@ -24,13 +24,13 @@ export function AssignmentExpression(node, parent, scope, file) { var nodes = []; var left = node.left.object; - var temp = scope.generateTempBasedOnNode(node.left.object); + var temp = scope.generateMemoisedReference(left); nodes.push(t.expressionStatement(t.assignmentExpression("=", temp, left))); nodes.push(buildDefaultsCallExpression(node, temp, file)); if (temp) nodes.push(temp); - return t.toSequenceExpression(nodes); + return nodes; } export function ExpressionStatement(node, parent, scope, file) { diff --git a/src/babel/traversal/path/index.js b/src/babel/traversal/path/index.js index 40248f93c4..9122169812 100644 --- a/src/babel/traversal/path/index.js +++ b/src/babel/traversal/path/index.js @@ -9,6 +9,36 @@ import extend from "lodash/object/extend"; import Scope from "../scope"; import * as t from "../../types"; +var hoistVariablesVisitor = { + enter(node, parent, scope ) { + if (this.isFunction()) { + return this.skip(); + } + + if (this.isVariableDeclaration() && node.kind === "var") { + var bindings = this.getBindingIdentifiers(); + for (var key in bindings) { + scope.push({ + id: bindings[key].identifiers + }); + } + + var exprs = []; + + for (var i = 0; i < node.declarations.length; i++) { + var declar = node.declarations[i]; + if (declar.init) { + exprs.push(t.expressionStatement( + t.assignmentExpression("=", declar.id, declar.init) + )); + } + } + + return exprs; + } + } +}; + export default class TraversalPath { constructor(parent, container) { this.container = container; @@ -86,6 +116,8 @@ export default class TraversalPath { } this.setScope(file); + + this.type = this.node && this.node.type; } remove() { @@ -130,16 +162,28 @@ export default class TraversalPath { set node(replacement) { if (!replacement) return this.remove(); - var oldNode = this.node; - var isArray = Array.isArray(replacement); + var oldNode = this.node; + + var isArray = Array.isArray(replacement); + if (isArray && replacement.length === 1) { + isArray = false; + replacement = replacement[0]; + } + var replacements = isArray ? replacement : [replacement]; // inherit comments from original node to the first replacement node var inheritTo = replacements[0]; if (inheritTo) t.inheritsComments(inheritTo, oldNode); + // + if (t.isStatement(replacements[0]) && t.isType(this.type, "Expression")) { + return this.setStatementsToExpression(replacements); + } + // replace the node this.container[this.key] = replacement; + this.type = replacement.type; // potentially create new scope this.setScope(); @@ -162,6 +206,50 @@ export default class TraversalPath { } } + getLastStatements(): Array { + var paths = []; + + var add = function (path) { + paths = paths.concat(path.getLastStatements()); + }; + + if (this.isIfStatement()) { + add(this.get("consequent")); + add(this.get("alternate")); + } else if (this.isFor() || this.isWhile()) { + add(this.get("body")); + } else if (this.isProgram() || this.isBlockStatement()) { + add(this.get("body").pop()); + } + + return paths; + } + + setStatementsToExpression(nodes: Array) { + var toSequenceExpression = t.toSequenceExpression(nodes, this.scope); + + if (toSequenceExpression) { + return this.node = toSequenceExpression; + } else { + var container = t.shadowFunctionExpression(null, [], t.blockStatement(nodes)); + + this.node = t.callExpression(container, []); + + // add implicit returns to all ending expression statements + var last = this.getLastStatements(); + for (var i = 0; i < last.length; i++) { + var lastNode = last[i]; + if (lastNode.isExpressionStatement()) { + lastNode.node = t.returnStatement(lastNode.node.expression); + } + } + + this.traverse(hoistVariablesVisitor); + + return this.node; + } + } + call(key) { var node = this.node; if (!node) return; diff --git a/src/babel/traversal/scope.js b/src/babel/traversal/scope.js index ba3364b6a4..e5c5adad87 100644 --- a/src/babel/traversal/scope.js +++ b/src/babel/traversal/scope.js @@ -198,8 +198,8 @@ export default class Scope { * Description */ - generateTempBasedOnNode(node: Object): ?Object { - if (t.isThisExpression(node) || t.isSuperExpression(node)) { + generateMemoisedReference(node: Object, dontPush?: boolean): ?Object { + if (t.isThisExpression(node) || t.isSuper(node)) { return null; } @@ -208,10 +208,7 @@ export default class Scope { } var id = this.generateUidBasedOnNode(node); - this.push({ - key: id.name, - id: id - }); + if (!dontPush) this.push({ id }); return id; } @@ -513,7 +510,7 @@ export default class Scope { getFunctionParent() { var scope = this; - while (scope.parent && !t.isFunction(scope.block)) { + while (scope.parent && !t.isFunction(scope.block) && !t.isShadowFunctionExpression(scope.block)) { scope = scope.parent; } return scope; diff --git a/src/babel/types/alias-keys.json b/src/babel/types/alias-keys.json index 67e5f891a6..a8b227ddae 100644 --- a/src/babel/types/alias-keys.json +++ b/src/babel/types/alias-keys.json @@ -53,6 +53,7 @@ "ArrayExpression": ["Expression"], "AssignmentExpression": ["Expression"], "AwaitExpression": ["Expression"], + "ShadowFunctionExpression": ["Expression"], "CallExpression": ["Expression"], "ComprehensionExpression": ["Expression", "Scopable"], "ConditionalExpression": ["Expression"], @@ -64,7 +65,7 @@ "SequenceExpression": ["Expression"], "TaggedTemplateExpression": ["Expression"], "ThisExpression": ["Expression"], - "SuperExpression": ["Expression"], + "Super": ["Expression"], "UpdateExpression": ["Expression"], "JSXEmptyExpression": ["Expression"], "JSXMemberExpression": ["Expression"], diff --git a/src/babel/types/builder-keys.json b/src/babel/types/builder-keys.json index 65fee94c49..b273d89207 100644 --- a/src/babel/types/builder-keys.json +++ b/src/babel/types/builder-keys.json @@ -45,6 +45,13 @@ "tokens": null }, + "ShadowFunctionExpression": { + "id": null, + "params": null, + "body": null, + "generator": false + }, + "FunctionExpression": { "id": null, "params": null, diff --git a/src/babel/types/converters.js b/src/babel/types/converters.js index 429e9c45dd..8d653566c6 100644 --- a/src/babel/types/converters.js +++ b/src/babel/types/converters.js @@ -27,31 +27,46 @@ export function toComputedKey(node: Object, key: Object = node.key || node.prope */ export function toSequenceExpression(nodes: Array, scope: Scope): Object { - var exprs = []; + var declars = []; + var exprs = []; - each(nodes, function (node) { + for (let i = 0; i < nodes.length; i++) { + var node = nodes[i]; if (t.isExpression(node)) { exprs.push(node); - } if (t.isExpressionStatement(node)) { + } else if (t.isExpressionStatement(node)) { exprs.push(node.expression); } else if (t.isVariableDeclaration(node)) { + if (node.kind !== "var") return; // bailed + each(node.declarations, function (declar) { - scope.push({ + declars.push({ kind: node.kind, id: declar.id }); exprs.push(t.assignmentExpression("=", declar.id, declar.init)); }); } else if (t.isIfStatement(node)) { - return t.conditionalExpression( + exprs.push(t.conditionalExpression( node.test, node.consequent ? t.toSequenceExpression([node.consequent]) : t.identifier("undefined"), node.alternate ? t.toSequenceExpression([node.alternate]) : t.identifier("undefined") - ); + )); } else if (t.isBlockStatement(node)) { - return t.toSequenceExpression(node.body); + exprs.push(t.toSequenceExpression(node.body)); + } else { + // bailed, we can't understand this + return; } - }); + } + + // + + for (let i = 0; i < declars.length; i++) { + scope.push(declars[i]); + } + + // if (exprs.length === 1) { return exprs[0]; diff --git a/src/babel/types/index.js b/src/babel/types/index.js index cc93dbb9df..de5e5a7773 100644 --- a/src/babel/types/index.js +++ b/src/babel/types/index.js @@ -24,7 +24,6 @@ function registerType(type: string, skipAliasCheck?: boolean) { }; } - export var STATEMENT_OR_BLOCK_KEYS = ["consequent", "body", "alternate"]; export var NATIVE_TYPE_NAMES = ["Array", "Object", "Number", "Boolean", "Date", "Array", "String", "Promise", "Set", "Map", "WeakMap", "WeakSet", "Uint16Array", "ArrayBuffer", "DataView", "Int8Array", "Uint8Array", "Uint8ClampedArray", "Uint32Array", "Int32Array", "Float32Array", "Int16Array", "Float64Array"]; export var FLATTENABLE_KEYS = ["body", "expressions"]; @@ -65,25 +64,23 @@ export const TYPES = Object.keys(t.VISITOR_KEYS).concat(Object.keys(t.FLIPPED_AL export function is(type: string, node: Object, opts?: Object, skipAliasCheck?: boolean): boolean { if (!node) return false; - var typeMatches = type === node.type; + var matches = isType(node.type, type); + if (!matches) return false; - if (!typeMatches && !skipAliasCheck) { - var aliases = t.FLIPPED_ALIAS_KEYS[type]; - - if (typeof aliases !== "undefined") { - typeMatches = aliases.indexOf(node.type) > -1; - } - } - - if (!typeMatches) { - return false; - } - - if (typeof opts !== "undefined") { + if (typeof opts === "undefined") { + return true; + } else { return t.shallowEqual(node, opts); } +} - return true; +export function isType(nodeType, targetType) { + if (nodeType === targetType) return true; + + var aliases = t.FLIPPED_ALIAS_KEYS[targetType]; + if (aliases) return aliases.indexOf(nodeType) > -1; + + return false; } each(t.VISITOR_KEYS, function (keys, type) {