UNPKG

9.05 kBJavaScriptView Raw
1/*istanbul ignore next*/"use strict";
2
3var /*istanbul ignore next*/_assert = require("assert");
4
5/*istanbul ignore next*/
6var _assert2 = _interopRequireDefault(_assert);
7
8var /*istanbul ignore next*/_babelTypes = require("babel-types");
9
10/*istanbul ignore next*/
11var t = _interopRequireWildcard(_babelTypes);
12
13var /*istanbul ignore next*/_hoist = require("./hoist");
14
15var /*istanbul ignore next*/_emit = require("./emit");
16
17var /*istanbul ignore next*/_util = require("./util");
18
19/*istanbul ignore next*/
20var util = _interopRequireWildcard(_util);
21
22/*istanbul ignore next*/
23function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
24
25function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
26
27var getMarkInfo = require("private").makeAccessor(); /**
28 * Copyright (c) 2014, Facebook, Inc.
29 * All rights reserved.
30 *
31 * This source code is licensed under the BSD-style license found in the
32 * https://raw.github.com/facebook/regenerator/master/LICENSE file. An
33 * additional grant of patent rights can be found in the PATENTS file in
34 * the same directory.
35 */
36
37exports.visitor = {
38 Function: {
39 exit: function /*istanbul ignore next*/exit(path, state) {
40 var node = path.node;
41
42 if (node.generator) {
43 if (node.async) {
44 // Async generator
45 if (state.opts.asyncGenerators === false) return;
46 } else {
47 // Plain generator
48 if (state.opts.generators === false) return;
49 }
50 } else if (node.async) {
51 // Async function
52 if (state.opts.async === false) return;
53 } else {
54 // Not a generator or async function.
55 return;
56 }
57
58 var contextId = path.scope.generateUidIdentifier("context");
59 var argsId = path.scope.generateUidIdentifier("args");
60
61 path.ensureBlock();
62 var bodyBlockPath = path.get("body");
63
64 if (node.async) {
65 bodyBlockPath.traverse(awaitVisitor);
66 }
67
68 bodyBlockPath.traverse(functionSentVisitor, {
69 context: contextId
70 });
71
72 var outerBody = [];
73 var innerBody = [];
74
75 bodyBlockPath.get("body").forEach(function (childPath) {
76 var node = childPath.node;
77 if (node && node._blockHoist != null) {
78 outerBody.push(node);
79 } else {
80 innerBody.push(node);
81 }
82 });
83
84 if (outerBody.length > 0) {
85 // Only replace the inner body if we actually hoisted any statements
86 // to the outer body.
87 bodyBlockPath.node.body = innerBody;
88 }
89
90 var outerFnExpr = getOuterFnExpr(path);
91 // Note that getOuterFnExpr has the side-effect of ensuring that the
92 // function has a name (so node.id will always be an Identifier), even
93 // if a temporary name has to be synthesized.
94 t.assertIdentifier(node.id);
95 var innerFnId = t.identifier(node.id.name + "$");
96
97 // Turn all declarations into vars, and replace the original
98 // declarations with equivalent assignment expressions.
99 var vars = /*istanbul ignore next*/(0, _hoist.hoist)(path);
100
101 var didRenameArguments = renameArguments(path, argsId);
102 if (didRenameArguments) {
103 vars = vars || t.variableDeclaration("var", []);
104 vars.declarations.push(t.variableDeclarator(argsId, t.identifier("arguments")));
105 }
106
107 var emitter = new /*istanbul ignore next*/_emit.Emitter(contextId);
108 emitter.explode(path.get("body"));
109
110 if (vars && vars.declarations.length > 0) {
111 outerBody.push(vars);
112 }
113
114 var wrapArgs = [emitter.getContextFunction(innerFnId),
115 // Async functions that are not generators don't care about the
116 // outer function because they don't need it to be marked and don't
117 // inherit from its .prototype.
118 node.generator ? outerFnExpr : t.nullLiteral(), t.thisExpression()];
119
120 var tryLocsList = emitter.getTryLocsList();
121 if (tryLocsList) {
122 wrapArgs.push(tryLocsList);
123 }
124
125 var wrapCall = t.callExpression(util.runtimeProperty(node.async ? "async" : "wrap"), wrapArgs);
126
127 outerBody.push(t.returnStatement(wrapCall));
128 node.body = t.blockStatement(outerBody);
129
130 var wasGeneratorFunction = node.generator;
131 if (wasGeneratorFunction) {
132 node.generator = false;
133 }
134
135 if (node.async) {
136 node.async = false;
137 }
138
139 if (wasGeneratorFunction && t.isExpression(node)) {
140 path.replaceWith(t.callExpression(util.runtimeProperty("mark"), [node]));
141 }
142
143 // Generators are processed in 'exit' handlers so that regenerator only has to run on
144 // an ES5 AST, but that means traversal will not pick up newly inserted references
145 // to things like 'regeneratorRuntime'. To avoid this, we explicitly requeue.
146 path.requeue();
147 }
148 }
149};
150
151// Given a NodePath for a Function, return an Expression node that can be
152// used to refer reliably to the function object from inside the function.
153// This expression is essentially a replacement for arguments.callee, with
154// the key advantage that it works in strict mode.
155function getOuterFnExpr(funPath) {
156 var node = funPath.node;
157 t.assertFunction(node);
158
159 if (!node.id) {
160 // Default-exported function declarations, and function expressions may not
161 // have a name to reference, so we explicitly add one.
162 node.id = funPath.scope.parent.generateUidIdentifier("callee");
163 }
164
165 if (node.generator && // Non-generator functions don't need to be marked.
166 t.isFunctionDeclaration(node)) {
167 var pp = funPath.findParent(function (path) {
168 return path.isProgram() || path.isBlockStatement();
169 });
170
171 if (!pp) {
172 return node.id;
173 }
174
175 var markDecl = getRuntimeMarkDecl(pp);
176 var markedArray = markDecl.declarations[0].id;
177 var funDeclIdArray = markDecl.declarations[0].init.callee.object;
178 t.assertArrayExpression(funDeclIdArray);
179
180 var index = funDeclIdArray.elements.length;
181 funDeclIdArray.elements.push(node.id);
182
183 return t.memberExpression(markedArray, t.numericLiteral(index), true);
184 }
185
186 return node.id;
187}
188
189function getRuntimeMarkDecl(blockPath) {
190 var block = blockPath.node;
191 /*istanbul ignore next*/_assert2.default.ok(Array.isArray(block.body));
192
193 var info = getMarkInfo(block);
194 if (info.decl) {
195 return info.decl;
196 }
197
198 info.decl = t.variableDeclaration("var", [t.variableDeclarator(blockPath.scope.generateUidIdentifier("marked"), t.callExpression(t.memberExpression(t.arrayExpression([]), t.identifier("map"), false), [util.runtimeProperty("mark")]))]);
199
200 blockPath.unshiftContainer("body", info.decl);
201
202 return info.decl;
203}
204
205function renameArguments(funcPath, argsId) {
206 var state = {
207 didRenameArguments: false,
208 argsId: argsId
209 };
210
211 funcPath.traverse(argumentsVisitor, state);
212
213 // If the traversal replaced any arguments references, then we need to
214 // alias the outer function's arguments binding (be it the implicit
215 // arguments object or some other parameter or variable) to the variable
216 // named by argsId.
217 return state.didRenameArguments;
218}
219
220var argumentsVisitor = {
221 "FunctionExpression|FunctionDeclaration": function /*istanbul ignore next*/FunctionExpressionFunctionDeclaration(path) {
222 path.skip();
223 },
224
225 Identifier: function /*istanbul ignore next*/Identifier(path, state) {
226 if (path.node.name === "arguments" && util.isReference(path)) {
227 path.replaceWith(state.argsId);
228 state.didRenameArguments = true;
229 }
230 }
231};
232
233var functionSentVisitor = { /*istanbul ignore next*/
234 MetaProperty: function MetaProperty(path) {
235 /*istanbul ignore next*/var node = path.node;
236
237
238 if (node.meta.name === "function" && node.property.name === "sent") {
239 path.replaceWith(t.memberExpression(this.context, t.identifier("_sent")));
240 }
241 }
242};
243
244var awaitVisitor = {
245 Function: function /*istanbul ignore next*/Function(path) {
246 path.skip(); // Don't descend into nested function scopes.
247 },
248
249 AwaitExpression: function /*istanbul ignore next*/AwaitExpression(path) {
250 // Convert await expressions to yield expressions.
251 var argument = path.node.argument;
252
253 // Transforming `await x` to `yield regeneratorRuntime.awrap(x)`
254 // causes the argument to be wrapped in such a way that the runtime
255 // can distinguish between awaited and merely yielded values.
256 path.replaceWith(t.yieldExpression(t.callExpression(util.runtimeProperty("awrap"), [argument]), false));
257 }
258};
\No newline at end of file