diff --git a/lib/6to5/transformation/transformers/es6-generators.js b/lib/6to5/transformation/transformers/es6-generators.js new file mode 100644 index 0000000000..7b7c0a3f0e --- /dev/null +++ b/lib/6to5/transformation/transformers/es6-generators.js @@ -0,0 +1 @@ +module.exports = require("regenerator").transform; diff --git a/lib/6to5/transformation/transformers/es6-generators/emit/explode-expressions.js b/lib/6to5/transformation/transformers/es6-generators/emit/explode-expressions.js deleted file mode 100644 index fe28897340..0000000000 --- a/lib/6to5/transformation/transformers/es6-generators/emit/explode-expressions.js +++ /dev/null @@ -1,204 +0,0 @@ -/** - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * https://raw.github.com/facebook/regenerator/master/LICENSE file. An - * additional grant of patent rights can be found in the PATENTS file in - * the same directory. - */ - -var assert = require("assert"); -var loc = require("../util").loc; -var t = require("../../../../types"); - -exports.ParenthesizedExpression = function (expr, path, explodeViaTempVar, finish) { - return finish(this.explodeExpression(path.get("expression"))); -}; - -exports.MemberExpression = function (expr, path, explodeViaTempVar, finish) { - return finish(t.memberExpression( - this.explodeExpression(path.get("object")), - expr.computed ? explodeViaTempVar(null, path.get("property")) : expr.property, - expr.computed - )); -}; - -exports.CallExpression = function (expr, path, explodeViaTempVar, finish) { - var oldCalleePath = path.get("callee"); - var newCallee = this.explodeExpression(oldCalleePath); - - // If the callee was not previously a MemberExpression, then the - // CallExpression was "unqualified," meaning its `this` object should - // be the global object. If the exploded expression has become a - // MemberExpression, then we need to force it to be unqualified by - // using the (0, object.property)(...) trick; otherwise, it will - // receive the object of the MemberExpression as its `this` object. - if (!t.isMemberExpression(oldCalleePath.node) && t.isMemberExpression(newCallee)) { - newCallee = t.sequenceExpression([ - t.literal(0), - newCallee - ]); - } - - return finish(t.callExpression( - newCallee, - path.get("arguments").map(function (argPath) { - return explodeViaTempVar(null, argPath); - }) - )); -}; - -exports.NewExpression = function (expr, path, explodeViaTempVar, finish) { - return finish(t.newExpression( - explodeViaTempVar(null, path.get("callee")), - path.get("arguments").map(function (argPath) { - return explodeViaTempVar(null, argPath); - }) - )); -}; - -exports.ObjectExpression = function (expr, path, explodeViaTempVar, finish) { - return finish(t.objectExpression( - path.get("properties").map(function (propPath) { - return t.property( - propPath.value.kind, - propPath.value.key, - explodeViaTempVar(null, propPath.get("value")) - ); - }) - )); -}; - -exports.ArrayExpression = function (expr, path, explodeViaTempVar, finish) { - return finish(t.arrayExpression( - path.get("elements").map(function (elemPath) { - return explodeViaTempVar(null, elemPath); - }) - )); -}; - -exports.SequenceExpression = function (expr, path, explodeViaTempVar, finish, ignoreResult) { - var lastIndex = expr.expressions.length - 1; - var self = this; - var result; - - path.get("expressions").each(function (exprPath) { - if (exprPath.name === lastIndex) { - result = self.explodeExpression(exprPath, ignoreResult); - } else { - self.explodeExpression(exprPath, true); - } - }); - - return result; -}; - -exports.LogicalExpression = function (expr, path, explodeViaTempVar, finish, ignoreResult) { - var after = loc(); - var result; - - if (!ignoreResult) { - result = this.makeTempVar(); - } - - var left = explodeViaTempVar(result, path.get("left")); - - if (expr.operator === "&&") { - this.jumpIfNot(left, after); - } else { - assert.strictEqual(expr.operator, "||"); - this.jumpIf(left, after); - } - - explodeViaTempVar(result, path.get("right"), ignoreResult); - - this.mark(after); - - return result; -}; - -exports.ConditionalExpression = function (expr, path, explodeViaTempVar, finish, ignoreResult) { - var elseLoc = loc(); - var after = loc(); - var test = this.explodeExpression(path.get("test")); - var result; - - this.jumpIfNot(test, elseLoc); - - if (!ignoreResult) { - result = this.makeTempVar(); - } - - explodeViaTempVar(result, path.get("consequent"), ignoreResult); - this.jump(after); - - this.mark(elseLoc); - explodeViaTempVar(result, path.get("alternate"), ignoreResult); - - this.mark(after); - - return result; -}; - -exports.UnaryExpression = function (expr, path, explodeViaTempVar, finish) { - return finish(t.unaryExpression( - expr.operator, - // Can't (and don't need to) break up the syntax of the argument. - // Think about delete a[b]. - this.explodeExpression(path.get("argument")), - !!expr.prefix - )); -}; - -exports.BinaryExpression = function (expr, path, explodeViaTempVar, finish) { - return finish(t.binaryExpression( - expr.operator, - explodeViaTempVar(null, path.get("left")), - explodeViaTempVar(null, path.get("right")) - )); -}; - -exports.AssignmentExpression = function (expr, path, explodeViaTempVar, finish) { - return finish(t.assignmentExpression( - expr.operator, - this.explodeExpression(path.get("left")), - this.explodeExpression(path.get("right")) - )); -}; - -exports.UpdateExpression = function (expr, path, explodeViaTempVar, finish) { - return finish(t.updateExpression( - expr.operator, - this.explodeExpression(path.get("argument")), - expr.prefix - )); -}; - -exports.YieldExpression = function (expr, path) { - var after = loc(); - var arg = expr.argument && this.explodeExpression(path.get("argument")); - var result; - - if (arg && expr.delegate) { - result = this.makeTempVar(); - - this.emit(t.returnStatement(t.callExpression( - this.contextProperty("delegateYield"), [ - arg, - t.literal(result.property.name), - after - ] - ))); - - this.mark(after); - - return result; - } - - this.emitAssign(this.contextProperty("next"), after); - this.emit(t.returnStatement(arg || null)); - this.mark(after); - - return this.contextProperty("sent"); -}; diff --git a/lib/6to5/transformation/transformers/es6-generators/emit/explode-statements.js b/lib/6to5/transformation/transformers/es6-generators/emit/explode-statements.js deleted file mode 100644 index 896d461561..0000000000 --- a/lib/6to5/transformation/transformers/es6-generators/emit/explode-statements.js +++ /dev/null @@ -1,335 +0,0 @@ -/** - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * https://raw.github.com/facebook/regenerator/master/LICENSE file. An - * additional grant of patent rights can be found in the PATENTS file in - * the same directory. - */ - -var assert = require("assert"); -var types = require("ast-types"); -var leap = require("../leap"); -var util = require("../util"); -var t = require("../../../../types"); - -var runtimeKeysMethod = util.runtimeProperty("keys"); -var loc = util.loc; - -exports.ExpressionStatement = function (path) { - this.explodeExpression(path.get("expression"), true); -}; - -exports.LabeledStatement = function (path, stmt) { - this.explodeStatement(path.get("body"), stmt.label); -}; - -exports.WhileStatement = function (path, stmt, labelId) { - var before = loc(); - var after = loc(); - - this.mark(before); - this.jumpIfNot(this.explodeExpression(path.get("test")), after); - this.leapManager.withEntry( - new leap.LoopEntry(after, before, labelId), - function () { this.explodeStatement(path.get("body")); } - ); - this.jump(before); - this.mark(after); -}; - -exports.DoWhileStatement = function (path, stmt, labelId) { - var first = loc(); - var test = loc(); - var after = loc(); - - this.mark(first); - this.leapManager.withEntry( - new leap.LoopEntry(after, test, labelId), - function () { this.explode(path.get("body")); } - ); - this.mark(test); - this.jumpIf(this.explodeExpression(path.get("test")), first); - this.mark(after); -}; - -exports.ForStatement = function (path, stmt, labelId) { - var head = loc(); - var update = loc(); - var after = loc(); - - if (stmt.init) { - // We pass true here to indicate that if stmt.init is an expression - // then we do not care about its result. - this.explode(path.get("init"), true); - } - - this.mark(head); - - if (stmt.test) { - this.jumpIfNot(this.explodeExpression(path.get("test")), after); - } else { - // No test means continue unconditionally. - } - - this.leapManager.withEntry( - new leap.LoopEntry(after, update, labelId), - function () { this.explodeStatement(path.get("body")); } - ); - - this.mark(update); - - if (stmt.update) { - // We pass true here to indicate that if stmt.update is an - // expression then we do not care about its result. - this.explode(path.get("update"), true); - } - - this.jump(head); - - this.mark(after); -}; - -exports.ForInStatement = function (path, stmt, labelId) { - t.assertIdentifier(stmt.left); - - var head = loc(); - var after = loc(); - - var keyIterNextFn = this.makeTempVar(); - this.emitAssign( - keyIterNextFn, - t.callExpression( - runtimeKeysMethod, - [this.explodeExpression(path.get("right"))] - ) - ); - - this.mark(head); - - var keyInfoTmpVar = this.makeTempVar(); - this.jumpIf( - t.memberExpression( - t.assignmentExpression( - "=", - keyInfoTmpVar, - t.callExpression(keyIterNextFn, []) - ), - t.identifier("done"), - false - ), - after - ); - - this.emitAssign( - stmt.left, - t.memberExpression( - keyInfoTmpVar, - t.identifier("value"), - false - ) - ); - - this.leapManager.withEntry( - new leap.LoopEntry(after, head, labelId), - function () { this.explodeStatement(path.get("body")); } - ); - - this.jump(head); - - this.mark(after); -}; - -exports.BreakStatement = function (path, stmt) { - this.emitAbruptCompletion({ - type: "break", - target: this.leapManager.getBreakLoc(stmt.label) - }); -}; - -exports.ContinueStatement = function (path, stmt) { - this.emitAbruptCompletion({ - type: "continue", - target: this.leapManager.getContinueLoc(stmt.label) - }); -}; - -exports.SwitchStatement = function (path, stmt) { - // Always save the discriminant into a temporary variable in case the - // test expressions overwrite values like context.sent. - var disc = this.emitAssign( - this.makeTempVar(), - this.explodeExpression(path.get("discriminant")) - ); - - var after = loc(); - var defaultLoc = loc(); - var condition = defaultLoc; - var caseLocs = []; - var self = this; - - // If there are no cases, .cases might be undefined. - var cases = stmt.cases || []; - - for (var i = cases.length - 1; i >= 0; --i) { - var c = cases[i]; - t.assertSwitchCase(c); - - if (c.test) { - condition = t.conditionalExpression( - t.binaryExpression("===", disc, c.test), - caseLocs[i] = loc(), - condition - ); - } else { - caseLocs[i] = defaultLoc; - } - } - - this.jump(this.explodeExpression( - new types.NodePath(condition, path, "discriminant") - )); - - this.leapManager.withEntry( - new leap.SwitchEntry(after), - function () { - path.get("cases").each(function (casePath) { - var i = casePath.name; - - self.mark(caseLocs[i]); - - casePath.get("consequent").each( - self.explodeStatement, - self - ); - }); - } - ); - - this.mark(after); - if (defaultLoc.value === -1) { - this.mark(defaultLoc); - assert.strictEqual(after.value, defaultLoc.value); - } -}; - -exports.IfStatement = function (path, stmt) { - var elseLoc = stmt.alternate && loc(); - var after = loc(); - - this.jumpIfNot( - this.explodeExpression(path.get("test")), - elseLoc || after - ); - - this.explodeStatement(path.get("consequent")); - - if (elseLoc) { - this.jump(after); - this.mark(elseLoc); - this.explodeStatement(path.get("alternate")); - } - - this.mark(after); -}; - -exports.ReturnStatement = function (path) { - this.emitAbruptCompletion({ - type: "return", - value: this.explodeExpression(path.get("argument")) - }); -}; - -exports.TryStatement = function (path, stmt) { - var after = loc(); - var self = this; - - var handler = stmt.handler; - if (!handler && stmt.handlers) { - handler = stmt.handlers[0] || null; - } - - var catchLoc = handler && loc(); - var catchEntry = catchLoc && new leap.CatchEntry( - catchLoc, - handler.param - ); - - var finallyLoc = stmt.finalizer && loc(); - var finallyEntry = finallyLoc && new leap.FinallyEntry(finallyLoc); - - var tryEntry = new leap.TryEntry( - this.getUnmarkedCurrentLoc(), - catchEntry, - finallyEntry - ); - - this.tryEntries.push(tryEntry); - this.updateContextPrevLoc(tryEntry.firstLoc); - - this.leapManager.withEntry(tryEntry, function () { - this.explodeStatement(path.get("block")); - - if (catchLoc) { - if (finallyLoc) { - // If we have both a catch block and a finally block, then - // because we emit the catch block first, we need to jump over - // it to the finally block. - this.jump(finallyLoc); - - } else { - // If there is no finally block, then we need to jump over the - // catch block to the fall-through location. - this.jump(after); - } - - this.updateContextPrevLoc(self.mark(catchLoc)); - - var bodyPath = path.get("handler", "body"); - var safeParam = this.makeTempVar(); - this.clearPendingException(tryEntry.firstLoc, safeParam); - - var catchScope = bodyPath.scope; - var catchParamName = handler.param.name; - t.assertCatchClause(catchScope.node); - assert.strictEqual(catchScope.lookup(catchParamName), catchScope); - - types.visit(bodyPath, { - visitIdentifier: function (path) { - if (t.isReferenced(path.value, path.parentPath.node) && - path.value.name === catchParamName && - path.scope.lookup(catchParamName) === catchScope) { - return safeParam; - } - this.traverse(path); - } - }); - - this.leapManager.withEntry(catchEntry, function () { - this.explodeStatement(bodyPath); - }); - } - - if (finallyLoc) { - this.updateContextPrevLoc(this.mark(finallyLoc)); - - this.leapManager.withEntry(finallyEntry, function () { - this.explodeStatement(path.get("finalizer")); - }); - - this.emit(t.callExpression( - this.contextProperty("finish"), - [finallyEntry.firstLoc] - )); - } - }); - - this.mark(after); -}; - -exports.ThrowStatement = function (path) { - this.emit(t.throwStatement( - this.explodeExpression(path.get("argument")) - )); -}; diff --git a/lib/6to5/transformation/transformers/es6-generators/emit/index.js b/lib/6to5/transformation/transformers/es6-generators/emit/index.js deleted file mode 100644 index 8df1576721..0000000000 --- a/lib/6to5/transformation/transformers/es6-generators/emit/index.js +++ /dev/null @@ -1,611 +0,0 @@ -/** - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * https://raw.github.com/facebook/regenerator/master/LICENSE file. An - * additional grant of patent rights can be found in the PATENTS file in - * the same directory. - */ - -exports.Emitter = Emitter; - -var explodeExpressions = require("./explode-expressions"); -var explodeStatements = require("./explode-statements"); -var assert = require("assert"); -var types = require("ast-types"); -var leap = require("../leap"); -var meta = require("../meta"); -var util = require("../util"); -var t = require("../../../../types"); -var _ = require("lodash"); - -var loc = util.loc; -var n = types.namedTypes; - -function Emitter(contextId) { - assert.ok(this instanceof Emitter); - t.assertIdentifier(contextId); - - // In order to make sure the context object does not collide with - // anything in the local scope, we might have to rename it, so we - // refer to it symbolically instead of just assuming that it will be - // called "context". - this.contextId = contextId; - - // An append-only list of Statements that grows each time this.emit is - // called. - this.listing = []; - - // A sparse array whose keys correspond to locations in this.listing - // that have been marked as branch/jump targets. - this.marked = [true]; - - // The last location will be marked when this.getDispatchLoop is - // called. - this.finalLoc = loc(); - - // A list of all leap.TryEntry statements emitted. - this.tryEntries = []; - - // Each time we evaluate the body of a loop, we tell this.leapManager - // to enter a nested loop context that determines the meaning of break - // and continue statements therein. - this.leapManager = new leap.LeapManager(this); -} - -// Sets the exact value of the given location to the offset of the next -// Statement emitted. -Emitter.prototype.mark = function (loc) { - t.assertLiteral(loc); - var index = this.listing.length; - if (loc.value === -1) { - loc.value = index; - } else { - // Locations can be marked redundantly, but their values cannot change - // once set the first time. - assert.strictEqual(loc.value, index); - } - this.marked[index] = true; - return loc; -}; - -Emitter.prototype.emit = function (node) { - if (t.isExpression(node)) node = t.expressionStatement(node); - t.assertStatement(node); - this.listing.push(node); -}; - -// Shorthand for emitting assignment statements. This will come in handy -// for assignments to temporary variables. -Emitter.prototype.emitAssign = function (lhs, rhs) { - this.emit(this.assign(lhs, rhs)); - return lhs; -}; - -// Shorthand for an assignment statement. -Emitter.prototype.assign = function (lhs, rhs) { - return t.expressionStatement( - t.assignmentExpression("=", lhs, rhs)); -}; - -// Convenience function for generating expressions like context.next, -// context.sent, and context.rval. -Emitter.prototype.contextProperty = function (name, computed) { - return t.memberExpression( - this.contextId, - computed ? t.literal(name) : t.identifier(name), - !!computed - ); -}; - -var volatileContextPropertyNames = { - prev: true, - next: true, - sent: true, - rval: true -}; - -// A "volatile" context property is a MemberExpression like context.sent -// that should probably be stored in a temporary variable when there's a -// possibility the property will get overwritten. -Emitter.prototype.isVolatileContextProperty = function (expr) { - if (t.isMemberExpression(expr)) { - if (expr.computed) { - // If it's a computed property such as context[couldBeAnything], - // assume the worst in terms of volatility. - return true; - } - - if (t.isIdentifier(expr.object) && - t.isIdentifier(expr.property) && - expr.object.name === this.contextId.name && - _.has(volatileContextPropertyNames, expr.property.name)) { - return true; - } - } - - return false; -}; - -// Shorthand for setting context.rval and jumping to `context.stop()`. -Emitter.prototype.stop = function (rval) { - if (rval) { - this.setReturnValue(rval); - } - - this.jump(this.finalLoc); -}; - -Emitter.prototype.setReturnValue = function (valuePath) { - t.assertExpression(valuePath.value); - - this.emitAssign( - this.contextProperty("rval"), - this.explodeExpression(valuePath) - ); -}; - -Emitter.prototype.clearPendingException = function (tryLoc, assignee) { - t.assertLiteral(tryLoc); - - var catchCall = t.callExpression( - this.contextProperty("catch", true), - [tryLoc] - ); - - if (assignee) { - this.emitAssign(assignee, catchCall); - } else { - this.emit(catchCall); - } -}; - -// Emits code for an unconditional jump to the given location, even if the -// exact value of the location is not yet known. -Emitter.prototype.jump = function (toLoc) { - this.emitAssign(this.contextProperty("next"), toLoc); - this.emit(t.breakStatement()); -}; - -// Conditional jump. -Emitter.prototype.jumpIf = function (test, toLoc) { - t.assertExpression(test); - t.assertLiteral(toLoc); - - this.emit(t.ifStatement( - test, - t.blockStatement([ - this.assign(this.contextProperty("next"), toLoc), - t.breakStatement() - ]) - )); -}; - -// Conditional jump, with the condition negated. -Emitter.prototype.jumpIfNot = function (test, toLoc) { - t.assertExpression(test); - t.assertLiteral(toLoc); - - var negatedTest; - if (t.isUnaryExpression(test) && test.operator === "!") { - // Avoid double negation. - negatedTest = test.argument; - } else { - negatedTest = t.unaryExpression("!", test); - } - - this.emit(t.ifStatement( - negatedTest, - t.blockStatement([ - this.assign(this.contextProperty("next"), toLoc), - t.breakStatement() - ]) - )); -}; - -// Returns a unique MemberExpression that can be used to store and -// retrieve temporary values. Since the object of the member expression is -// the context object, which is presumed to coexist peacefully with all -// other local variables, and since we just increment `nextTempId` -// monotonically, uniqueness is assured. -var nextTempId = 0; -Emitter.prototype.makeTempVar = function () { - return this.contextProperty("t" + nextTempId++); -}; - -Emitter.prototype.getContextFunction = function (id) { - var node = t.functionExpression( - id || null, - [this.contextId], - t.blockStatement([this.getDispatchLoop()]), - false, // Not a generator anymore! - false // Nor an expression. - ); - node._aliasFunction = true; - return node; -}; - -// Turns this.listing into a loop of the form -// -// while (1) switch (context.next) { -// case 0: -// ... -// case n: -// return context.stop(); -// } -// -// Each marked location in this.listing will correspond to one generated -// case statement. -Emitter.prototype.getDispatchLoop = function () { - var self = this; - var cases = []; - var current; - - // If we encounter a break, continue, or return statement in a switch - // case, we can skip the rest of the statements until the next case. - var alreadyEnded = false; - - self.listing.forEach(function (stmt, i) { - if (self.marked.hasOwnProperty(i)) { - cases.push(t.switchCase(t.literal(i), current = [])); - alreadyEnded = false; - } - - if (!alreadyEnded) { - current.push(stmt); - if (isSwitchCaseEnder(stmt)) - alreadyEnded = true; - } - }); - - // Now that we know how many statements there will be in this.listing, - // we can finally resolve this.finalLoc.value. - this.finalLoc.value = this.listing.length; - - cases.push( - t.switchCase(this.finalLoc, [ - // Intentionally fall through to the "end" case... - ]), - - // So that the runtime can jump to the final location without having - // to know its offset, we provide the "end" case as a synonym. - t.switchCase(t.literal("end"), [ - // This will check/clear both context.thrown and context.rval. - t.returnStatement( - t.callExpression(this.contextProperty("stop"), []) - ) - ]) - ); - - return t.whileStatement( - t.literal(true), - t.switchStatement( - t.assignmentExpression( - "=", - this.contextProperty("prev"), - this.contextProperty("next") - ), - cases - ) - ); -}; - -// See comment above re: alreadyEnded. -function isSwitchCaseEnder(stmt) { - return t.isBreakStatement(stmt) || - t.isContinueStatement(stmt) || - t.isReturnStatement(stmt) || - t.isThrowStatement(stmt); -} - -Emitter.prototype.getTryEntryList = function () { - if (this.tryEntries.length === 0) { - // To avoid adding a needless [] to the majority of runtime.wrap - // argument lists, force the caller to handle this case specially. - return null; - } - - var lastLocValue = 0; - - return t.arrayExpression( - this.tryEntries.map(function (tryEntry) { - var thisLocValue = tryEntry.firstLoc.value; - assert.ok(thisLocValue >= lastLocValue, "try entries out of order"); - lastLocValue = thisLocValue; - - var ce = tryEntry.catchEntry; - var fe = tryEntry.finallyEntry; - - var triple = [ - tryEntry.firstLoc, - // The null here makes a hole in the array. - ce ? ce.firstLoc : null - ]; - - if (fe) { - triple[2] = fe.firstLoc; - } - - return t.arrayExpression(triple); - }) - ); -}; - -// All side effects must be realized in order. - -// If any subexpression harbors a leap, all subexpressions must be -// neutered of side effects. - -// No destructive modification of AST nodes. - -Emitter.prototype.explode = function (path, ignoreResult) { - assert.ok(path instanceof types.NodePath); - - var node = path.value; - var self = this; - - n.Node.check(node); - - if (t.isStatement(node)) - return self.explodeStatement(path); - - if (t.isExpression(node)) - return self.explodeExpression(path, ignoreResult); - - if (t.isDeclaration(node)) - throw getDeclError(node); - - switch (node.type) { - case "Program": - return path.get("body").map(self.explodeStatement, self); - - case "VariableDeclarator": - throw getDeclError(node); - - // These node types should be handled by their parent nodes - // (ObjectExpression, SwitchStatement, and TryStatement, respectively). - case "Property": - case "SwitchCase": - case "CatchClause": - throw new Error(node.type + " nodes should be handled by their parents"); - - default: - throw new Error("unknown Node of type " + JSON.stringify(node.type)); - } -}; - -function getDeclError(node) { - return new Error( - "all declarations should have been transformed into " + - "assignments before the Exploder began its work: " + - JSON.stringify(node)); -} - -Emitter.prototype.explodeStatement = function (path, labelId) { - assert.ok(path instanceof types.NodePath); - - var stmt = path.value; - var self = this; - - t.assertStatement(stmt); - - if (labelId) { - t.assertIdentifier(labelId); - } else { - labelId = null; - } - - // Explode BlockStatement nodes even if they do not contain a yield, - // because we don't want or need the curly braces. - if (t.isBlockStatement(stmt)) { - return path.get("body").each( - self.explodeStatement, - self - ); - } - - if (!meta.containsLeap(stmt)) { - // Technically we should be able to avoid emitting the statement - // altogether if !meta.hasSideEffects(stmt), but that leads to - // confusing generated code (for instance, `while (true) {}` just - // disappears) and is probably a more appropriate job for a dedicated - // dead code elimination pass. - self.emit(stmt); - return; - } - - var fn = explodeStatements[stmt.type]; - if (fn) { - fn.call(this, path, stmt, labelId); - } else { - throw new Error("unknown Statement of type " + JSON.stringify(stmt.type)); - } -}; - -Emitter.prototype.emitAbruptCompletion = function (record) { - if (!isValidCompletion(record)) { - assert.ok( - false, - "invalid completion record: " + JSON.stringify(record) - ); - } - - assert.notStrictEqual( - record.type, "normal", - "normal completions are not abrupt" - ); - - var abruptArgs = [t.literal(record.type)]; - - if (record.type === "break" || record.type === "continue") { - t.assertLiteral(record.target); - abruptArgs[1] = record.target; - } else if (record.type === "return" || record.type === "throw") { - if (record.value) { - t.assertExpression(record.value); - abruptArgs[1] = record.value; - } - } - - this.emit( - t.returnStatement( - t.callExpression( - this.contextProperty("abrupt"), - abruptArgs - ) - ) - ); -}; - -function isValidCompletion(record) { - var type = record.type; - - if (type === "normal") { - return !_.has(record, "target"); - } - - if (type === "break" || type === "continue") { - return !_.has(record, "value") && t.isLiteral(record.target); - } - - if (type === "return" || type === "throw") { - return _.has(record, "value") && !_.has(record, "target"); - } - - return false; -} - -// Not all offsets into emitter.listing are potential jump targets. For -// example, execution typically falls into the beginning of a try block -// without jumping directly there. This method returns the current offset -// without marking it, so that a switch case will not necessarily be -// generated for this offset (I say "not necessarily" because the same -// location might end up being marked in the process of emitting other -// statements). There's no logical harm in marking such locations as jump -// targets, but minimizing the number of switch cases keeps the generated -// code shorter. -Emitter.prototype.getUnmarkedCurrentLoc = function () { - return t.literal(this.listing.length); -}; - -// The context.prev property takes the value of context.next whenever we -// evaluate the switch statement discriminant, which is generally good -// enough for tracking the last location we jumped to, but sometimes -// context.prev needs to be more precise, such as when we fall -// successfully out of a try block and into a finally block without -// jumping. This method exists to update context.prev to the freshest -// available location. If we were implementing a full interpreter, we -// would know the location of the current instruction with complete -// precision at all times, but we don't have that luxury here, as it would -// be costly and verbose to set context.prev before every statement. -Emitter.prototype.updateContextPrevLoc = function (loc) { - if (loc) { - t.assertLiteral(loc); - - if (loc.value === -1) { - // If an uninitialized location literal was passed in, set its value - // to the current this.listing.length. - loc.value = this.listing.length; - } else { - // Otherwise assert that the location matches the current offset. - assert.strictEqual(loc.value, this.listing.length); - } - - } else { - loc = this.getUnmarkedCurrentLoc(); - } - - // Make sure context.prev is up to date in case we fell into this try - // statement without jumping to it. TODO Consider avoiding this - // assignment when we know control must have jumped here. - this.emitAssign(this.contextProperty("prev"), loc); -}; - -Emitter.prototype.explodeExpression = function (path, ignoreResult) { - assert.ok(path instanceof types.NodePath); - - var expr = path.value; - if (expr) { - t.assertExpression(expr); - } else { - return expr; - } - - var self = this; - - function finish(expr) { - t.assertExpression(expr); - if (ignoreResult) { - self.emit(expr); - } else { - return expr; - } - } - - // If the expression does not contain a leap, then we either emit the - // expression as a standalone statement or return it whole. - if (!meta.containsLeap(expr)) { - return finish(expr); - } - - // If any child contains a leap (such as a yield or labeled continue or - // break statement), then any sibling subexpressions will almost - // certainly have to be exploded in order to maintain the order of their - // side effects relative to the leaping child(ren). - var hasLeapingChildren = meta.containsLeap.onlyChildren(expr); - - // In order to save the rest of explodeExpression from a combinatorial - // trainwreck of special cases, explodeViaTempVar is responsible for - // deciding when a subexpression needs to be "exploded," which is my - // very technical term for emitting the subexpression as an assignment - // to a temporary variable and the substituting the temporary variable - // for the original subexpression. Think of exploded view diagrams, not - // Michael Bay movies. The point of exploding subexpressions is to - // control the precise order in which the generated code realizes the - // side effects of those subexpressions. - function explodeViaTempVar(tempVar, childPath, ignoreChildResult) { - assert.ok(childPath instanceof types.NodePath); - - assert.ok( - !ignoreChildResult || !tempVar, - "Ignoring the result of a child expression but forcing it to " + - "be assigned to a temporary variable?" - ); - - var result = self.explodeExpression(childPath, ignoreChildResult); - - if (ignoreChildResult) { - // Side effects already emitted above. - - } else if (tempVar || (hasLeapingChildren && - (self.isVolatileContextProperty(result) || - meta.hasSideEffects(result)))) { - // If tempVar was provided, then the result will always be assigned - // to it, even if the result does not otherwise need to be assigned - // to a temporary variable. When no tempVar is provided, we have - // the flexibility to decide whether a temporary variable is really - // necessary. In general, temporary assignment is required only - // when some other child contains a leap and the child in question - // is a context property like $ctx.sent that might get overwritten - // or an expression with side effects that need to occur in proper - // sequence relative to the leap. - result = self.emitAssign( - tempVar || self.makeTempVar(), - result - ); - } - return result; - } - - // If ignoreResult is true, then we must take full responsibility for - // emitting the expression with all its side effects, and we should not - // return a result. - - var fn = explodeExpressions[expr.type]; - if (fn) { - return fn.call(this, expr, path, explodeViaTempVar, finish, ignoreResult); - } else { - throw new Error("unknown Expression of type " + JSON.stringify(expr.type)); - } -}; diff --git a/lib/6to5/transformation/transformers/es6-generators/hoist.js b/lib/6to5/transformation/transformers/es6-generators/hoist.js deleted file mode 100644 index b9772466ba..0000000000 --- a/lib/6to5/transformation/transformers/es6-generators/hoist.js +++ /dev/null @@ -1,151 +0,0 @@ -/** - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * https://raw.githut.com/facebook/regenerator/master/LICENSE file. An - * additional grant of patent rights can be found in the PATENTS file in - * the same directory. - */ - -var assert = require("assert"); -var types = require("ast-types"); -var t = require("../../../types"); -var _ = require("lodash"); - -// The hoist function takes a FunctionExpression or FunctionDeclaration -// and replaces any Declaration nodes in its body with assignments, then -// returns a VariableDeclaration containing just the names of the removed -// declarations. -exports.hoist = function (funPath) { - assert.ok(funPath instanceof types.NodePath); - t.assertFunction(funPath.value); - - var vars = {}; - - function varDeclToExpr(vdec, includeIdentifiers) { - t.assertVariableDeclaration(vdec); - var exprs = []; - - vdec.declarations.forEach(function (dec) { - vars[dec.id.name] = dec.id; - - if (dec.init) { - exprs.push(t.assignmentExpression( - "=", dec.id, dec.init - )); - } else if (includeIdentifiers) { - exprs.push(dec.id); - } - }); - - if (exprs.length === 0) - return null; - - if (exprs.length === 1) - return exprs[0]; - - return t.sequenceExpression(exprs); - } - - types.visit(funPath.get("body"), { - visitVariableDeclaration: function (path) { - var expr = varDeclToExpr(path.value, false); - if (expr === null) { - path.replace(); - } else { - // We don't need to traverse this expression any further because - // there can't be any new declarations inside an expression. - return t.expressionStatement(expr); - } - - // Since the original node has been either removed or replaced, - // avoid traversing it any further. - return false; - }, - - visitForStatement: function (path) { - var init = path.value.init; - if (t.isVariableDeclaration(init)) { - path.get("init").replace(varDeclToExpr(init, false)); - } - this.traverse(path); - }, - - visitForInStatement: function (path) { - var left = path.value.left; - if (t.isVariableDeclaration(left)) { - path.get("left").replace(varDeclToExpr(left, true)); - } - this.traverse(path); - }, - - visitFunctionDeclaration: function (path) { - var node = path.value; - vars[node.id.name] = node.id; - - var assignment = t.expressionStatement( - t.assignmentExpression( - "=", - node.id, - t.functionExpression( - node.id, - node.params, - node.body, - node.generator, - node.expression - ) - ) - ); - - if (t.isBlockStatement(path.parent.node)) { - // Insert the assignment form before the first statement in the - // enclosing block. - path.parent.get("body").unshift(assignment); - - // Remove the function declaration now that we've inserted the - // equivalent assignment form at the beginning of the block. - path.replace(); - - } else { - // If the parent node is not a block statement, then we can just - // replace the declaration with the equivalent assignment form - // without worrying about hoisting it. - path.replace(assignment); - } - - // Don't hoist variables out of inner functions. - return false; - }, - - visitFunctionExpression: function () { - // Don't descend into nested function expressions. - return false; - } - }); - - var paramNames = {}; - funPath.get("params").each(function (paramPath) { - var param = paramPath.value; - if (t.isIdentifier(param)) { - paramNames[param.name] = param; - } else { - // Variables declared by destructuring parameter patterns will be - // harmlessly re-declared. - } - }); - - var declarations = []; - - Object.keys(vars).forEach(function (name) { - if (!_.has(paramNames, name)) { - declarations.push(t.variableDeclarator(vars[name], null)); - } - }); - - if (declarations.length === 0) { - return null; // Be sure to handle this case! - } - - return t.variableDeclaration("var", declarations); -}; diff --git a/lib/6to5/transformation/transformers/es6-generators/index.js b/lib/6to5/transformation/transformers/es6-generators/index.js deleted file mode 100644 index 5d2aa5acba..0000000000 --- a/lib/6to5/transformation/transformers/es6-generators/index.js +++ /dev/null @@ -1,3 +0,0 @@ -var t = require("../../../types"); - -exports.ast = require("./visit").transform; diff --git a/lib/6to5/transformation/transformers/es6-generators/leap.js b/lib/6to5/transformation/transformers/es6-generators/leap.js deleted file mode 100644 index 5cd289b501..0000000000 --- a/lib/6to5/transformation/transformers/es6-generators/leap.js +++ /dev/null @@ -1,163 +0,0 @@ -/** - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * https://raw.github.com/facebook/regenerator/master/LICENSE file. An - * additional grant of patent rights can be found in the PATENTS file in - * the same directory. - */ - -exports.FunctionEntry = FunctionEntry; -exports.FinallyEntry = FinallyEntry; -exports.SwitchEntry = SwitchEntry; -exports.LeapManager = LeapManager; -exports.CatchEntry = CatchEntry; -exports.LoopEntry = LoopEntry; -exports.TryEntry = TryEntry; - -var assert = require("assert"); -var util = require("util"); -var t = require("../../../types"); - -var inherits = util.inherits; - -function Entry() { - assert.ok(this instanceof Entry); -} - -function FunctionEntry(returnLoc) { - Entry.call(this); - - t.assertLiteral(returnLoc); - - this.returnLoc = returnLoc; -} - -inherits(FunctionEntry, Entry); - -function LoopEntry(breakLoc, continueLoc, label) { - Entry.call(this); - - t.assertLiteral(breakLoc); - t.assertLiteral(continueLoc); - - if (label) { - t.assertIdentifier(label); - } else { - label = null; - } - - this.breakLoc = breakLoc; - this.continueLoc = continueLoc; - this.label = label; -} - -inherits(LoopEntry, Entry); - -function SwitchEntry(breakLoc) { - Entry.call(this); - - t.assertLiteral(breakLoc); - - this.breakLoc = breakLoc; -} - -inherits(SwitchEntry, Entry); - -function TryEntry(firstLoc, catchEntry, finallyEntry) { - Entry.call(this); - - t.assertLiteral(firstLoc); - - if (catchEntry) { - assert.ok(catchEntry instanceof CatchEntry); - } else { - catchEntry = null; - } - - if (finallyEntry) { - assert.ok(finallyEntry instanceof FinallyEntry); - } else { - finallyEntry = null; - } - - // Have to have one or the other (or both). - assert.ok(catchEntry || finallyEntry); - - this.firstLoc = firstLoc; - this.catchEntry = catchEntry; - this.finallyEntry = finallyEntry; -} - -inherits(TryEntry, Entry); - -function CatchEntry(firstLoc, paramId) { - Entry.call(this); - - t.assertLiteral(firstLoc); - t.assertIdentifier(paramId); - - this.firstLoc = firstLoc; - this.paramId = paramId; -} - -inherits(CatchEntry, Entry); - -function FinallyEntry(firstLoc) { - Entry.call(this); - - t.assertLiteral(firstLoc); - - this.firstLoc = firstLoc; -} - -inherits(FinallyEntry, Entry); - -function LeapManager(emitter) { - assert.ok(this instanceof LeapManager); - - var Emitter = require("./emit").Emitter; - assert.ok(emitter instanceof Emitter); - - this.emitter = emitter; - this.entryStack = [new FunctionEntry(emitter.finalLoc)]; -} - -LeapManager.prototype.withEntry = function (entry, callback) { - assert.ok(entry instanceof Entry); - this.entryStack.push(entry); - try { - callback.call(this.emitter); - } finally { - var popped = this.entryStack.pop(); - assert.strictEqual(popped, entry); - } -}; - -LeapManager.prototype._findLeapLocation = function (property, label) { - for (var i = this.entryStack.length - 1; i >= 0; --i) { - var entry = this.entryStack[i]; - var loc = entry[property]; - if (loc) { - if (label) { - if (entry.label && - entry.label.name === label.name) { - return loc; - } - } else { - return loc; - } - } - } - - return null; -}; - -LeapManager.prototype.getBreakLoc = function (label) { - return this._findLeapLocation("breakLoc", label); -}; - -LeapManager.prototype.getContinueLoc = function (label) { - return this._findLeapLocation("continueLoc", label); -}; diff --git a/lib/6to5/transformation/transformers/es6-generators/meta.js b/lib/6to5/transformation/transformers/es6-generators/meta.js deleted file mode 100644 index f7bb1248e6..0000000000 --- a/lib/6to5/transformation/transformers/es6-generators/meta.js +++ /dev/null @@ -1,98 +0,0 @@ -/** - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * https://raw.github.com/facebook/regenerator/master/LICENSE file. An - * additional grant of patent rights can be found in the PATENTS file in - * the same directory. - */ - -var assert = require("assert"); -var types = require("ast-types"); -var m = require("private").makeAccessor(); -var _ = require("lodash"); - -var isArray = types.builtInTypes.array; -var n = types.namedTypes; - -function makePredicate(propertyName, knownTypes) { - function onlyChildren(node) { - n.Node.check(node); - - // Assume no side effects until we find out otherwise. - var result = false; - - function check(child) { - if (result) { - // Do nothing. - } else if (isArray.check(child)) { - child.some(check); - } else if (n.Node.check(child)) { - assert.strictEqual(result, false); - result = predicate(child); - } - return result; - } - - types.eachField(node, function (name, child) { - check(child); - }); - - return result; - } - - function predicate(node) { - n.Node.check(node); - - var meta = m(node); - if (_.has(meta, propertyName)) return meta[propertyName]; - - // Certain types are "opaque," which means they have no side - // effects or leaps and we don't care about their subexpressions. - if (_.has(opaqueTypes, node.type)) return meta[propertyName] = false; - - if (_.has(knownTypes, node.type)) return meta[propertyName] = true; - - return meta[propertyName] = onlyChildren(node); - } - - predicate.onlyChildren = onlyChildren; - - return predicate; -} - -var opaqueTypes = { - FunctionExpression: true -}; - -// These types potentially have side effects regardless of what side -// effects their subexpressions have. -var sideEffectTypes = { - CallExpression: true, // Anything could happen! - ForInStatement: true, // Modifies the key variable. - UnaryExpression: true, // Think delete. - BinaryExpression: true, // Might invoke .toString() or .valueOf(). - AssignmentExpression: true, // Side-effecting by definition. - UpdateExpression: true, // Updates are essentially assignments. - NewExpression: true // Similar to CallExpression. -}; - -// These types are the direct cause of all leaps in control flow. -var leapTypes = { - YieldExpression: true, - BreakStatement: true, - ContinueStatement: true, - ReturnStatement: true, - ThrowStatement: true -}; - -// All leap types are also side effect types. -for (var type in leapTypes) { - if (_.has(leapTypes, type)) { - sideEffectTypes[type] = leapTypes[type]; - } -} - -exports.hasSideEffects = makePredicate("hasSideEffects", sideEffectTypes); -exports.containsLeap = makePredicate("containsLeap", leapTypes); diff --git a/lib/6to5/transformation/transformers/es6-generators/runtime.js b/lib/6to5/transformation/transformers/es6-generators/runtime.js deleted file mode 100644 index 9fe09b03cb..0000000000 --- a/lib/6to5/transformation/transformers/es6-generators/runtime.js +++ /dev/null @@ -1,447 +0,0 @@ -/** -* Copyright (c) 2014, Facebook, Inc. -* All rights reserved. -* -* This source code is licensed under the BSD-style license found in the -* https://raw.github.com/facebook/regenerator/master/LICENSE file. An -* additional grant of patent rights can be found in the PATENTS file in -* the same directory. -*/ - -var iteratorSymbol = typeof Symbol === "function" && Symbol.iterator || "@@iterator"; -var runtime = global.regeneratorRuntime = exports; -var hasOwn = Object.prototype.hasOwnProperty; - -var wrap = runtime.wrap = function wrap(innerFn, outerFn, self, tryList) { - return new Generator(innerFn, outerFn, self || null, tryList || []); -}; - -var GenStateSuspendedStart = "suspendedStart"; -var GenStateSuspendedYield = "suspendedYield"; -var GenStateExecuting = "executing"; -var GenStateCompleted = "completed"; - -// Returning this object from the innerFn has the same effect as -// breaking out of the dispatch switch statement. -var ContinueSentinel = {}; - -// Dummy constructor that we use as the .constructor property for -// functions that return Generator objects. -function GeneratorFunction() {} -var GFp = function GeneratorFunctionPrototype() {}; -var Gp = GFp.prototype = Generator.prototype; -(GFp.constructor = GeneratorFunction).prototype = Gp.constructor = GFp; - -runtime.isGeneratorFunction = function (genFun) { - var ctor = genFun && genFun.constructor; - return ctor ? GeneratorFunction.name === ctor.name : false; -}; - -runtime.mark = function (genFun) { - genFun.__proto__ = GFp; - genFun.prototype = Object.create(Gp); - return genFun; -}; - -runtime.async = function (innerFn, outerFn, self, tryList) { - return new Promise(function (resolve, reject) { - var generator = wrap(innerFn, outerFn, self, tryList); - var callNext = step.bind(generator.next); - var callThrow = step.bind(generator["throw"]); - - function step(arg) { - var info; - var value; - - try { - info = this(arg); - value = info.value; - } catch (error) { - return reject(error); - } - - if (info.done) { - resolve(value); - } else { - Promise.resolve(value).then(callNext, callThrow); - } - } - - callNext(); - }); -}; - -function Generator(innerFn, outerFn, self, tryList) { - var generator = outerFn ? Object.create(outerFn.prototype) : this; - var context = new Context(tryList); - var state = GenStateSuspendedStart; - - function invoke(method, arg) { - if (state === GenStateExecuting) { - throw new Error("Generator is already running"); - } - - var alreadyFinished = state === GenStateCompleted; - - while (true) { - var delegate = context.delegate; - var info; - - if (delegate) { - try { - info = delegate.iterator[method](arg); - - // Delegate generator ran and handled its own exceptions so - // regardless of what the method was, we continue as if it is - // "next" with an undefined arg. - method = "next"; - arg = undefined; - - } catch (uncaught) { - context.delegate = null; - - // Like returning generator.throw(uncaught), but without the - // overhead of an extra function call. - method = "throw"; - arg = uncaught; - - continue; - } - - if (info.done) { - context[delegate.resultName] = info.value; - context.next = delegate.nextLoc; - } else { - state = GenStateSuspendedYield; - return info; - } - - context.delegate = null; - } - - if (method === "next") { - if (state === GenStateSuspendedStart && - typeof arg !== "undefined") { - // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-generatorresume - throw new TypeError( - "attempt to send " + JSON.stringify(arg) + " to newborn generator" - ); - } - - if (state === GenStateSuspendedYield) { - context.sent = arg; - } else { - delete context.sent; - } - - } else if (method === "throw") { - if (state === GenStateSuspendedStart) { - state = GenStateCompleted; - throw arg; - } - - if (context.dispatchException(arg)) { - // If the dispatched exception was caught by a catch block, - // then let that catch block handle the exception normally. - method = "next"; - arg = undefined; - } - - } else if (method === "return") { - context.abrupt("return", arg); - } - - state = GenStateExecuting; - - try { - var value; - if (!alreadyFinished) value = innerFn.call(self, context); - - // If an exception is thrown from innerFn, we leave state === - // GenStateExecuting and loop back for another invocation. - state = context.done ? GenStateCompleted : GenStateSuspendedYield; - - info = { - value: value, - done: context.done - }; - - if (value === ContinueSentinel) { - if (context.delegate && method === "next") { - // Deliberately forget the last sent value so that we don't - // accidentally pass it on to the delegate. - arg = undefined; - } - } else { - return info; - } - - } catch (thrown) { - state = GenStateCompleted; - - if (method === "next") { - context.dispatchException(thrown); - } else { - arg = thrown; - } - } - } - } - - generator.next = invoke.bind(generator, "next"); - generator["throw"] = invoke.bind(generator, "throw"); - generator["return"] = invoke.bind(generator, "return"); - - return generator; -} - -Gp[iteratorSymbol] = function () { - return this; -}; - -Gp.toString = function () { - return "[object Generator]"; -}; - -function pushTryEntry(triple) { - var entry = { tryLoc: triple[0] }; - - if (1 in triple) { - entry.catchLoc = triple[1]; - } - - if (2 in triple) { - entry.finallyLoc = triple[2]; - } - - this.tryEntries.push(entry); -} - -function resetTryEntry(entry, i) { - var record = entry.completion || {}; - record.type = i === 0 ? "normal" : "return"; - delete record.arg; - entry.completion = record; -} - -function Context(tryList) { - // The root entry object (effectively a try statement without a catch - // or a finally block) gives us a place to store values thrown from - // locations where there is no enclosing try statement. - this.tryEntries = [{ tryLoc: "root" }]; - tryList.forEach(pushTryEntry, this); - this.reset(); -} - -runtime.keys = function (object) { - var keys = []; - for (var key in object) { - keys.push(key); - } - keys.reverse(); - - // Rather than returning an object with a next method, we keep - // things simple and return the next function itself. - return function next() { - while (keys.length) { - var key = keys.pop(); - if (key in object) { - next.value = key; - next.done = false; - return next; - } - } - - // To avoid creating an additional object, we just hang the .value - // and .done properties off the next function object itself. This - // also ensures that the minifier will not anonymize the function. - next.done = true; - return next; - }; -}; - -function values(iterable) { - var iterator = iterable; - if (iterable[iteratorSymbol]) { - iterator = iterable[iteratorSymbol](); - } else if (!isNaN(iterable.length)) { - var i = -1; - iterator = function next() { - while (++i < iterable.length) { - if (i in iterable) { - next.value = iterable[i]; - next.done = false; - return next; - } - } - next.done = true; - return next; - }; - iterator.next = iterator; - } - return iterator; -} -runtime.values = values; - -Context.prototype = { - constructor: Context, - - reset: function () { - this.prev = 0; - this.next = 0; - this.sent = undefined; - this.done = false; - this.delegate = null; - - this.tryEntries.forEach(resetTryEntry); - - // Pre-initialize at least 20 temporary variables to enable hidden - // class optimizations for simple generators. - for (var tempIndex = 0, tempName; - hasOwn.call(this, tempName = "t" + tempIndex) || tempIndex < 20; - ++tempIndex) { - this[tempName] = null; - } - }, - - stop: function () { - this.done = true; - - var rootEntry = this.tryEntries[0]; - var rootRecord = rootEntry.completion; - if (rootRecord.type === "throw") { - throw rootRecord.arg; - } - - return this.rval; - }, - - dispatchException: function (exception) { - if (this.done) { - throw exception; - } - - var context = this; - function handle(loc, caught) { - record.type = "throw"; - record.arg = exception; - context.next = loc; - return !!caught; - } - - for (var i = this.tryEntries.length - 1; i >= 0; --i) { - var entry = this.tryEntries[i]; - var record = entry.completion; - - if (entry.tryLoc === "root") { - // Exception thrown outside of any try block that could handle - // it, so set the completion value of the entire function to - // throw the exception. - return handle("end"); - } - - if (entry.tryLoc <= this.prev) { - var hasCatch = hasOwn.call(entry, "catchLoc"); - var hasFinally = hasOwn.call(entry, "finallyLoc"); - - if (hasCatch && hasFinally) { - if (this.prev < entry.catchLoc) { - return handle(entry.catchLoc, true); - } else if (this.prev < entry.finallyLoc) { - return handle(entry.finallyLoc); - } - - } else if (hasCatch) { - if (this.prev < entry.catchLoc) { - return handle(entry.catchLoc, true); - } - - } else if (hasFinally) { - if (this.prev < entry.finallyLoc) { - return handle(entry.finallyLoc); - } - - } else { - throw new Error("try statement without catch or finally"); - } - } - } - }, - - _findFinallyEntry: function (finallyLoc) { - for (var i = this.tryEntries.length - 1; i >= 0; --i) { - var entry = this.tryEntries[i]; - if (entry.tryLoc <= this.prev && - hasOwn.call(entry, "finallyLoc") && ( - entry.finallyLoc === finallyLoc || - this.prev < entry.finallyLoc)) { - return entry; - } - } - }, - - abrupt: function (type, arg) { - var entry = this._findFinallyEntry(); - var record = entry ? entry.completion : {}; - - record.type = type; - record.arg = arg; - - if (entry) { - this.next = entry.finallyLoc; - } else { - this.complete(record); - } - - return ContinueSentinel; - }, - - complete: function (record) { - if (record.type === "throw") { - throw record.arg; - } - - if (record.type === "break" || record.type === "continue") { - this.next = record.arg; - } else if (record.type === "return") { - this.rval = record.arg; - this.next = "end"; - } - - return ContinueSentinel; - }, - - finish: function (finallyLoc) { - var entry = this._findFinallyEntry(finallyLoc); - return this.complete(entry.completion); - }, - - "catch": function (tryLoc) { - for (var i = this.tryEntries.length - 1; i >= 0; --i) { - var entry = this.tryEntries[i]; - if (entry.tryLoc === tryLoc) { - var record = entry.completion; - var thrown; - if (record.type === "throw") { - thrown = record.arg; - resetTryEntry(entry, i); - } - return thrown; - } - } - - // The context.catch method must only be called with a location - // argument that corresponds to a known catch block. - throw new Error("illegal catch attempt"); - }, - - delegateYield: function (iterable, resultName, nextLoc) { - this.delegate = { - iterator: values(iterable), - resultName: resultName, - nextLoc: nextLoc - }; - - return ContinueSentinel; - } -}; diff --git a/lib/6to5/transformation/transformers/es6-generators/util.js b/lib/6to5/transformation/transformers/es6-generators/util.js deleted file mode 100644 index 99e7072a19..0000000000 --- a/lib/6to5/transformation/transformers/es6-generators/util.js +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * https://raw.github.com/facebook/regenerator/master/LICENSE file. An - * additional grant of patent rights can be found in the PATENTS file in - * the same directory. - */ - -var t = require("../../../types"); - -exports.runtimeProperty = function (name) { - return t.memberExpression( - t.identifier("regeneratorRuntime"), - t.identifier(name) - ); -}; - -// Offsets into this.listing that could be used as targets for branches or -// jumps are represented as numeric Literal nodes. This representation has -// the amazingly convenient benefit of allowing the exact value of the -// location to be determined at any time, even after generating code that -// refers to the location. - -exports.loc = function () { - return t.literal(-1); -}; diff --git a/lib/6to5/transformation/transformers/es6-generators/visit.js b/lib/6to5/transformation/transformers/es6-generators/visit.js deleted file mode 100644 index eaa504fbab..0000000000 --- a/lib/6to5/transformation/transformers/es6-generators/visit.js +++ /dev/null @@ -1,221 +0,0 @@ -/** - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * https://raw.githut.com/facebook/regenerator/master/LICENSE file. An - * additional grant of patent rights can be found in the PATENTS file in - * the same directory. - */ - -var runtimeProperty = require("./util").runtimeProperty; -var Emitter = require("./emit").Emitter; -var hoist = require("./hoist").hoist; -var types = require("ast-types"); -var t = require("../../../types"); - -var runtimeAsyncMethod = runtimeProperty("async"); -var runtimeWrapMethod = runtimeProperty("wrap"); -var runtimeMarkMethod = runtimeProperty("mark"); - -exports.transform = function transform(node, file) { - return types.visit(node, { - visitFunction: function (path) { - return visitor.call(this, path, file); - } - }); -}; - -var visitor = function (path, file) { - // Calling this.traverse(path) first makes for a post-order traversal. - this.traverse(path); - - var node = path.value; - var scope; // we need to actually get the current scope - - if (!node.generator && !node.async) { - return; - } - - node.generator = false; - - if (node.expression) { - // Transform expression lambdas into normal functions. - node.expression = false; - node.body = t.blockStatement([ - t.returnStatement(node.body) - ]); - } - - if (node.async) { - awaitVisitor.visit(path.get("body")); - } - - var outerFnId = node.id || ( - node.id = file.generateUidIdentifier("callee", scope) - ); - - var innerFnId = t.identifier(node.id.name + "$"); - var contextId = file.generateUidIdentifier("context", scope); - var vars = hoist(path); - - var emitter = new Emitter(contextId); - emitter.explode(path.get("body")); - - var outerBody = []; - - if (vars && vars.declarations.length > 0) { - outerBody.push(vars); - } - - var wrapArgs = [ - emitter.getContextFunction(innerFnId), - // Async functions don't care about the outer function because they - // don't need it to be marked and don't inherit from its .prototype. - node.async ? t.literal(null) : outerFnId, - t.thisExpression() - ]; - - var tryEntryList = emitter.getTryEntryList(); - if (tryEntryList) { - wrapArgs.push(tryEntryList); - } - - var wrapCall = t.callExpression( - node.async ? runtimeAsyncMethod : runtimeWrapMethod, - wrapArgs - ); - - outerBody.push(t.returnStatement(wrapCall)); - node.body = t.blockStatement(outerBody); - - if (node.async) { - node.async = false; - return; - } - - if (t.isFunctionDeclaration(node)) { - var pp = path.parent; - - while (pp && !(t.isBlockStatement(pp.value) || t.isProgram(pp.value))) { - pp = pp.parent; - } - - if (!pp) { - return; - } - - // Here we turn the FunctionDeclaration into a named - // FunctionExpression that will be assigned to a variable of the - // same name at the top of the enclosing block. This is important - // for a very subtle reason: named function expressions can refer to - // themselves by name without fear that the binding may change due - // to code executing outside the function, whereas function - // declarations are vulnerable to the following rebinding: - // - // function f() { return f } - // var g = f; - // f = "asdf"; - // g(); // "asdf" - // - // One way to prevent the problem illustrated above is to transform - // the function declaration thus: - // - // var f = function f() { return f }; - // var g = f; - // f = "asdf"; - // g(); // f - // g()()()()(); // f - // - // In the code below, we transform generator function declarations - // in the following way: - // - // gen().next(); // { value: gen, done: true } - // function *gen() { - // return gen; - // } - // - // becomes something like - // - // var gen = runtime.mark(function *gen() { - // return gen; - // }); - // gen().next(); // { value: gen, done: true } - // - // which ensures that the generator body can always reliably refer - // to gen by name. - - // Remove the FunctionDeclaration so that we can add it back as a - // FunctionExpression passed to runtime.mark. - path.replace(); - - // Change the type of the function to be an expression instead of a - // declaration. Note that all the other fields are the same. - node.type = "FunctionExpression"; - - var varDecl = t.variableDeclaration("var", [ - t.variableDeclarator( - node.id, - t.callExpression(runtimeMarkMethod, [node]) - ) - ]); - - // Copy any comments preceding the function declaration to the - // variable declaration, to avoid weird formatting consequences. - t.inheritsComments(varDecl, node); - t.removeComments(node); - - varDecl._blockHoist = true; - - var bodyPath = pp.get("body"); - var bodyLen = bodyPath.value.length; - - for (var i = 0; i < bodyLen; ++i) { - var firstStmtPath = bodyPath.get(i); - if (!shouldNotHoistAbove(firstStmtPath)) { - firstStmtPath.insertBefore(varDecl); - return; - } - } - - bodyPath.push(varDecl); - } else { - t.assertFunctionExpression(node); - return t.callExpression(runtimeMarkMethod, [node]); - } -}; - -function shouldNotHoistAbove(stmtPath) { - var value = stmtPath.value; - t.assertStatement(value); - - // If the first statement is a "use strict" declaration, make sure to - // insert hoisted declarations afterwards. - if (t.isExpressionStatement(value) && - t.isLiteral(value.expression) && - value.expression.value === "use strict") { - return true; - } - - if (t.isVariableDeclaration(value)) { - for (var i = 0; i < value.declarations.length; ++i) { - var decl = value.declarations[i]; - if (t.isCallExpression(decl.init) && types.astNodesAreEquivalent(decl.init.callee, runtimeMarkMethod)) { - return true; - } - } - } - - return false; -} - -var awaitVisitor = types.PathVisitor.fromMethodsObject({ - visitFunction: function () { - return false; // Don't descend into nested function scopes. - }, - - visitAwaitExpression: function (path) { - // Convert await expressions to yield expressions. - return t.yieldExpression(path.value.argument, false); - } -}); diff --git a/package.json b/package.json index ef9868cc5e..77dcca580e 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ }, "dependencies": { "acorn-6to5": "0.9.1-14", - "ast-types": "0.6.5", + "ast-types": "~0.6.1", "chokidar": "0.11.1", "commander": "2.5.0", "es6-shim": "0.21.0", @@ -48,6 +48,7 @@ "lodash": "2.4.1", "mkdirp": "0.5.0", "private": "0.1.6", + "regenerator": "^0.8.2", "regexpu": "0.3.0", "roadrunner": "^1.0.4", "source-map": "0.1.40",