source: trip-planner-front/node_modules/regenerator-transform/src/visit.js@ 6c1585f

Last change on this file since 6c1585f was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 10.6 KB
Line 
1/**
2 * Copyright (c) 2014-present, Facebook, Inc.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8"use strict";
9
10import assert from "assert";
11import { hoist } from "./hoist";
12import { Emitter } from "./emit";
13import replaceShorthandObjectMethod from "./replaceShorthandObjectMethod";
14import * as util from "./util";
15
16exports.getVisitor = ({ types: t }) => ({
17 Method(path, state) {
18 let node = path.node;
19
20 if (!shouldRegenerate(node, state)) return;
21
22 const container = t.functionExpression(
23 null,
24 [],
25 t.cloneNode(node.body, false),
26 node.generator,
27 node.async,
28 );
29
30 path.get("body").set("body", [
31 t.returnStatement(
32 t.callExpression(container, []),
33 ),
34 ]);
35
36 // Regardless of whether or not the wrapped function is a an async method
37 // or generator the outer function should not be
38 node.async = false;
39 node.generator = false;
40
41 // Unwrap the wrapper IIFE's environment so super and this and such still work.
42 path
43 .get("body.body.0.argument.callee")
44 .unwrapFunctionEnvironment();
45 },
46 Function: {
47 exit: util.wrapWithTypes(t, function(path, state) {
48 let node = path.node;
49
50 if (!shouldRegenerate(node, state)) return;
51
52 // if this is an ObjectMethod, we need to convert it to an ObjectProperty
53 path = replaceShorthandObjectMethod(path);
54 node = path.node;
55
56 let contextId = path.scope.generateUidIdentifier("context");
57 let argsId = path.scope.generateUidIdentifier("args");
58
59 path.ensureBlock();
60 let bodyBlockPath = path.get("body");
61
62 if (node.async) {
63 bodyBlockPath.traverse(awaitVisitor);
64 }
65
66 bodyBlockPath.traverse(functionSentVisitor, {
67 context: contextId
68 });
69
70 let outerBody = [];
71 let innerBody = [];
72
73 bodyBlockPath.get("body").forEach(function(childPath) {
74 let node = childPath.node;
75 if (t.isExpressionStatement(node) &&
76 t.isStringLiteral(node.expression)) {
77 // Babylon represents directives like "use strict" as elements
78 // of a bodyBlockPath.node.directives array, but they could just
79 // as easily be represented (by other parsers) as traditional
80 // string-literal-valued expression statements, so we need to
81 // handle that here. (#248)
82 outerBody.push(node);
83 } else if (node && node._blockHoist != null) {
84 outerBody.push(node);
85 } else {
86 innerBody.push(node);
87 }
88 });
89
90 if (outerBody.length > 0) {
91 // Only replace the inner body if we actually hoisted any statements
92 // to the outer body.
93 bodyBlockPath.node.body = innerBody;
94 }
95
96 let outerFnExpr = getOuterFnExpr(path);
97 // Note that getOuterFnExpr has the side-effect of ensuring that the
98 // function has a name (so node.id will always be an Identifier), even
99 // if a temporary name has to be synthesized.
100 t.assertIdentifier(node.id);
101 let innerFnId = t.identifier(node.id.name + "$");
102
103 // Turn all declarations into vars, and replace the original
104 // declarations with equivalent assignment expressions.
105 let vars = hoist(path);
106
107 let context = {
108 usesThis: false,
109 usesArguments: false,
110 getArgsId: () => t.clone(argsId),
111 };
112 path.traverse(argumentsThisVisitor, context);
113
114 if (context.usesArguments) {
115 vars = vars || t.variableDeclaration("var", []);
116 vars.declarations.push(t.variableDeclarator(
117 t.clone(argsId),
118 t.identifier("arguments"),
119 ));
120 }
121
122 let emitter = new Emitter(contextId);
123 emitter.explode(path.get("body"));
124
125 if (vars && vars.declarations.length > 0) {
126 outerBody.push(vars);
127 }
128
129 let wrapArgs = [emitter.getContextFunction(innerFnId)];
130 let tryLocsList = emitter.getTryLocsList();
131
132 if (node.generator) {
133 wrapArgs.push(outerFnExpr);
134 } else if (context.usesThis || tryLocsList || node.async) {
135 // Async functions that are not generators don't care about the
136 // outer function because they don't need it to be marked and don't
137 // inherit from its .prototype.
138 wrapArgs.push(t.nullLiteral());
139 }
140 if (context.usesThis) {
141 wrapArgs.push(t.thisExpression());
142 } else if (tryLocsList || node.async) {
143 wrapArgs.push(t.nullLiteral());
144 }
145 if (tryLocsList) {
146 wrapArgs.push(tryLocsList);
147 } else if (node.async) {
148 wrapArgs.push(t.nullLiteral());
149 }
150
151 if (node.async) {
152 // Rename any locally declared "Promise" variable,
153 // to use the global one.
154 let currentScope = path.scope;
155 do {
156 if (currentScope.hasOwnBinding("Promise")) currentScope.rename("Promise");
157 } while (currentScope = currentScope.parent);
158
159 wrapArgs.push(t.identifier("Promise"));
160 }
161
162 let wrapCall = t.callExpression(
163 util.runtimeProperty(node.async ? "async" : "wrap"),
164 wrapArgs
165 );
166
167 outerBody.push(t.returnStatement(wrapCall));
168 node.body = t.blockStatement(outerBody);
169 // We injected a few new variable declarations (for every hoisted var),
170 // so we need to add them to the scope.
171 path.get("body.body").forEach(p => p.scope.registerDeclaration(p));
172
173 const oldDirectives = bodyBlockPath.node.directives;
174 if (oldDirectives) {
175 // Babylon represents directives like "use strict" as elements of
176 // a bodyBlockPath.node.directives array. (#248)
177 node.body.directives = oldDirectives;
178 }
179
180 let wasGeneratorFunction = node.generator;
181 if (wasGeneratorFunction) {
182 node.generator = false;
183 }
184
185 if (node.async) {
186 node.async = false;
187 }
188
189 if (wasGeneratorFunction && t.isExpression(node)) {
190 util.replaceWithOrRemove(path, t.callExpression(util.runtimeProperty("mark"), [node]))
191 path.addComment("leading", "#__PURE__");
192 }
193
194 const insertedLocs = emitter.getInsertedLocs();
195
196 path.traverse({
197 NumericLiteral(path) {
198 if (!insertedLocs.has(path.node)) {
199 return;
200 }
201
202 path.replaceWith(t.numericLiteral(path.node.value));
203 },
204 })
205
206 // Generators are processed in 'exit' handlers so that regenerator only has to run on
207 // an ES5 AST, but that means traversal will not pick up newly inserted references
208 // to things like 'regeneratorRuntime'. To avoid this, we explicitly requeue.
209 path.requeue();
210 })
211 }
212});
213
214// Check if a node should be transformed by regenerator
215function shouldRegenerate(node, state) {
216 if (node.generator) {
217 if (node.async) {
218 // Async generator
219 return state.opts.asyncGenerators !== false;
220 } else {
221 // Plain generator
222 return state.opts.generators !== false;
223 }
224 } else if (node.async) {
225 // Async function
226 return state.opts.async !== false;
227 } else {
228 // Not a generator or async function.
229 return false;
230 }
231}
232
233// Given a NodePath for a Function, return an Expression node that can be
234// used to refer reliably to the function object from inside the function.
235// This expression is essentially a replacement for arguments.callee, with
236// the key advantage that it works in strict mode.
237function getOuterFnExpr(funPath) {
238 const t = util.getTypes();
239 let node = funPath.node;
240 t.assertFunction(node);
241
242 if (!node.id) {
243 // Default-exported function declarations, and function expressions may not
244 // have a name to reference, so we explicitly add one.
245 node.id = funPath.scope.parent.generateUidIdentifier("callee");
246 }
247
248 if (node.generator && // Non-generator functions don't need to be marked.
249 t.isFunctionDeclaration(node)) {
250 // Return the identifier returned by runtime.mark(<node.id>).
251 return getMarkedFunctionId(funPath);
252 }
253
254 return t.clone(node.id);
255}
256
257const markInfo = new WeakMap();
258
259function getMarkInfo(node) {
260 if (!markInfo.has(node)) {
261 markInfo.set(node, {});
262 }
263 return markInfo.get(node);
264}
265
266function getMarkedFunctionId(funPath) {
267 const t = util.getTypes();
268 const node = funPath.node;
269 t.assertIdentifier(node.id);
270
271 const blockPath = funPath.findParent(function (path) {
272 return path.isProgram() || path.isBlockStatement();
273 });
274
275 if (!blockPath) {
276 return node.id;
277 }
278
279 const block = blockPath.node;
280 assert.ok(Array.isArray(block.body));
281
282 const info = getMarkInfo(block);
283 if (!info.decl) {
284 info.decl = t.variableDeclaration("var", []);
285 blockPath.unshiftContainer("body", info.decl);
286 info.declPath = blockPath.get("body.0");
287 }
288
289 assert.strictEqual(info.declPath.node, info.decl);
290
291 // Get a new unique identifier for our marked variable.
292 const markedId = blockPath.scope.generateUidIdentifier("marked");
293 const markCallExp = t.callExpression(
294 util.runtimeProperty("mark"),
295 [t.clone(node.id)]
296 );
297
298 const index = info.decl.declarations.push(
299 t.variableDeclarator(markedId, markCallExp)
300 ) - 1;
301
302 const markCallExpPath =
303 info.declPath.get("declarations." + index + ".init");
304
305 assert.strictEqual(markCallExpPath.node, markCallExp);
306
307 markCallExpPath.addComment("leading", "#__PURE__");
308
309 return t.clone(markedId);
310}
311
312let argumentsThisVisitor = {
313 "FunctionExpression|FunctionDeclaration|Method": function(path) {
314 path.skip();
315 },
316
317 Identifier: function(path, state) {
318 if (path.node.name === "arguments" && util.isReference(path)) {
319 util.replaceWithOrRemove(path, state.getArgsId());
320 state.usesArguments = true;
321 }
322 },
323
324 ThisExpression: function(path, state) {
325 state.usesThis = true;
326 }
327};
328
329let functionSentVisitor = {
330 MetaProperty(path) {
331 let { node } = path;
332
333 if (node.meta.name === "function" &&
334 node.property.name === "sent") {
335 const t = util.getTypes();
336 util.replaceWithOrRemove(
337 path,
338 t.memberExpression(
339 t.clone(this.context),
340 t.identifier("_sent")
341 )
342 );
343 }
344 }
345};
346
347let awaitVisitor = {
348 Function: function(path) {
349 path.skip(); // Don't descend into nested function scopes.
350 },
351
352 AwaitExpression: function(path) {
353 const t = util.getTypes();
354
355 // Convert await expressions to yield expressions.
356 let argument = path.node.argument;
357
358 // Transforming `await x` to `yield regeneratorRuntime.awrap(x)`
359 // causes the argument to be wrapped in such a way that the runtime
360 // can distinguish between awaited and merely yielded values.
361 util.replaceWithOrRemove(path, t.yieldExpression(
362 t.callExpression(
363 util.runtimeProperty("awrap"),
364 [argument]
365 ),
366 false
367 ));
368 }
369};
Note: See TracBrowser for help on using the repository browser.