1 | "use strict";
|
---|
2 |
|
---|
3 | Object.defineProperty(exports, "__esModule", {
|
---|
4 | value: true
|
---|
5 | });
|
---|
6 | exports.default = void 0;
|
---|
7 |
|
---|
8 | var _helperPluginUtils = require("@babel/helper-plugin-utils");
|
---|
9 |
|
---|
10 | var _tdz = require("./tdz");
|
---|
11 |
|
---|
12 | var _core = require("@babel/core");
|
---|
13 |
|
---|
14 | const DONE = new WeakSet();
|
---|
15 |
|
---|
16 | var _default = (0, _helperPluginUtils.declare)((api, opts) => {
|
---|
17 | api.assertVersion(7);
|
---|
18 | const {
|
---|
19 | throwIfClosureRequired = false,
|
---|
20 | tdz: tdzEnabled = false
|
---|
21 | } = opts;
|
---|
22 |
|
---|
23 | if (typeof throwIfClosureRequired !== "boolean") {
|
---|
24 | throw new Error(`.throwIfClosureRequired must be a boolean, or undefined`);
|
---|
25 | }
|
---|
26 |
|
---|
27 | if (typeof tdzEnabled !== "boolean") {
|
---|
28 | throw new Error(`.tdz must be a boolean, or undefined`);
|
---|
29 | }
|
---|
30 |
|
---|
31 | return {
|
---|
32 | name: "transform-block-scoping",
|
---|
33 | visitor: {
|
---|
34 | VariableDeclaration(path) {
|
---|
35 | const {
|
---|
36 | node,
|
---|
37 | parent,
|
---|
38 | scope
|
---|
39 | } = path;
|
---|
40 | if (!isBlockScoped(node)) return;
|
---|
41 | convertBlockScopedToVar(path, null, parent, scope, true);
|
---|
42 |
|
---|
43 | if (node._tdzThis) {
|
---|
44 | const nodes = [node];
|
---|
45 |
|
---|
46 | for (let i = 0; i < node.declarations.length; i++) {
|
---|
47 | const decl = node.declarations[i];
|
---|
48 |
|
---|
49 | const assign = _core.types.assignmentExpression("=", _core.types.cloneNode(decl.id), decl.init || scope.buildUndefinedNode());
|
---|
50 |
|
---|
51 | assign._ignoreBlockScopingTDZ = true;
|
---|
52 | nodes.push(_core.types.expressionStatement(assign));
|
---|
53 | decl.init = this.addHelper("temporalUndefined");
|
---|
54 | }
|
---|
55 |
|
---|
56 | node._blockHoist = 2;
|
---|
57 |
|
---|
58 | if (path.isCompletionRecord()) {
|
---|
59 | nodes.push(_core.types.expressionStatement(scope.buildUndefinedNode()));
|
---|
60 | }
|
---|
61 |
|
---|
62 | path.replaceWithMultiple(nodes);
|
---|
63 | }
|
---|
64 | },
|
---|
65 |
|
---|
66 | Loop(path, state) {
|
---|
67 | const {
|
---|
68 | parent,
|
---|
69 | scope
|
---|
70 | } = path;
|
---|
71 | path.ensureBlock();
|
---|
72 | const blockScoping = new BlockScoping(path, path.get("body"), parent, scope, throwIfClosureRequired, tdzEnabled, state);
|
---|
73 | const replace = blockScoping.run();
|
---|
74 | if (replace) path.replaceWith(replace);
|
---|
75 | },
|
---|
76 |
|
---|
77 | CatchClause(path, state) {
|
---|
78 | const {
|
---|
79 | parent,
|
---|
80 | scope
|
---|
81 | } = path;
|
---|
82 | const blockScoping = new BlockScoping(null, path.get("body"), parent, scope, throwIfClosureRequired, tdzEnabled, state);
|
---|
83 | blockScoping.run();
|
---|
84 | },
|
---|
85 |
|
---|
86 | "BlockStatement|SwitchStatement|Program"(path, state) {
|
---|
87 | if (!ignoreBlock(path)) {
|
---|
88 | const blockScoping = new BlockScoping(null, path, path.parent, path.scope, throwIfClosureRequired, tdzEnabled, state);
|
---|
89 | blockScoping.run();
|
---|
90 | }
|
---|
91 | }
|
---|
92 |
|
---|
93 | }
|
---|
94 | };
|
---|
95 | });
|
---|
96 |
|
---|
97 | exports.default = _default;
|
---|
98 |
|
---|
99 | function ignoreBlock(path) {
|
---|
100 | return _core.types.isLoop(path.parent) || _core.types.isCatchClause(path.parent);
|
---|
101 | }
|
---|
102 |
|
---|
103 | const buildRetCheck = (0, _core.template)(`
|
---|
104 | if (typeof RETURN === "object") return RETURN.v;
|
---|
105 | `);
|
---|
106 |
|
---|
107 | function isBlockScoped(node) {
|
---|
108 | if (!_core.types.isVariableDeclaration(node)) return false;
|
---|
109 | if (node[_core.types.BLOCK_SCOPED_SYMBOL]) return true;
|
---|
110 | if (node.kind !== "let" && node.kind !== "const") return false;
|
---|
111 | return true;
|
---|
112 | }
|
---|
113 |
|
---|
114 | function isInLoop(path) {
|
---|
115 | const loopOrFunctionParent = path.find(path => path.isLoop() || path.isFunction());
|
---|
116 | return loopOrFunctionParent == null ? void 0 : loopOrFunctionParent.isLoop();
|
---|
117 | }
|
---|
118 |
|
---|
119 | function convertBlockScopedToVar(path, node, parent, scope, moveBindingsToParent = false) {
|
---|
120 | if (!node) {
|
---|
121 | node = path.node;
|
---|
122 | }
|
---|
123 |
|
---|
124 | if (isInLoop(path) && !_core.types.isFor(parent)) {
|
---|
125 | for (let i = 0; i < node.declarations.length; i++) {
|
---|
126 | const declar = node.declarations[i];
|
---|
127 | declar.init = declar.init || scope.buildUndefinedNode();
|
---|
128 | }
|
---|
129 | }
|
---|
130 |
|
---|
131 | node[_core.types.BLOCK_SCOPED_SYMBOL] = true;
|
---|
132 | node.kind = "var";
|
---|
133 |
|
---|
134 | if (moveBindingsToParent) {
|
---|
135 | const parentScope = scope.getFunctionParent() || scope.getProgramParent();
|
---|
136 |
|
---|
137 | for (const name of Object.keys(path.getBindingIdentifiers())) {
|
---|
138 | const binding = scope.getOwnBinding(name);
|
---|
139 | if (binding) binding.kind = "var";
|
---|
140 | scope.moveBindingTo(name, parentScope);
|
---|
141 | }
|
---|
142 | }
|
---|
143 | }
|
---|
144 |
|
---|
145 | function isVar(node) {
|
---|
146 | return _core.types.isVariableDeclaration(node, {
|
---|
147 | kind: "var"
|
---|
148 | }) && !isBlockScoped(node);
|
---|
149 | }
|
---|
150 |
|
---|
151 | const letReferenceBlockVisitor = _core.traverse.visitors.merge([{
|
---|
152 | Loop: {
|
---|
153 | enter(path, state) {
|
---|
154 | state.loopDepth++;
|
---|
155 | },
|
---|
156 |
|
---|
157 | exit(path, state) {
|
---|
158 | state.loopDepth--;
|
---|
159 | }
|
---|
160 |
|
---|
161 | },
|
---|
162 |
|
---|
163 | FunctionParent(path, state) {
|
---|
164 | if (state.loopDepth > 0) {
|
---|
165 | path.traverse(letReferenceFunctionVisitor, state);
|
---|
166 | } else {
|
---|
167 | path.traverse(_tdz.visitor, state);
|
---|
168 | }
|
---|
169 |
|
---|
170 | return path.skip();
|
---|
171 | }
|
---|
172 |
|
---|
173 | }, _tdz.visitor]);
|
---|
174 |
|
---|
175 | const letReferenceFunctionVisitor = _core.traverse.visitors.merge([{
|
---|
176 | ReferencedIdentifier(path, state) {
|
---|
177 | const ref = state.letReferences.get(path.node.name);
|
---|
178 | if (!ref) return;
|
---|
179 | const localBinding = path.scope.getBindingIdentifier(path.node.name);
|
---|
180 | if (localBinding && localBinding !== ref) return;
|
---|
181 | state.closurify = true;
|
---|
182 | }
|
---|
183 |
|
---|
184 | }, _tdz.visitor]);
|
---|
185 |
|
---|
186 | const hoistVarDeclarationsVisitor = {
|
---|
187 | enter(path, self) {
|
---|
188 | if (path.isForStatement()) {
|
---|
189 | const {
|
---|
190 | node
|
---|
191 | } = path;
|
---|
192 |
|
---|
193 | if (isVar(node.init)) {
|
---|
194 | const nodes = self.pushDeclar(node.init);
|
---|
195 |
|
---|
196 | if (nodes.length === 1) {
|
---|
197 | node.init = nodes[0];
|
---|
198 | } else {
|
---|
199 | node.init = _core.types.sequenceExpression(nodes);
|
---|
200 | }
|
---|
201 | }
|
---|
202 | } else if (path.isForInStatement() || path.isForOfStatement()) {
|
---|
203 | const {
|
---|
204 | node
|
---|
205 | } = path;
|
---|
206 |
|
---|
207 | if (isVar(node.left)) {
|
---|
208 | self.pushDeclar(node.left);
|
---|
209 | node.left = node.left.declarations[0].id;
|
---|
210 | }
|
---|
211 | } else if (isVar(path.node)) {
|
---|
212 | path.replaceWithMultiple(self.pushDeclar(path.node).map(expr => _core.types.expressionStatement(expr)));
|
---|
213 | } else if (path.isFunction()) {
|
---|
214 | return path.skip();
|
---|
215 | }
|
---|
216 | }
|
---|
217 |
|
---|
218 | };
|
---|
219 | const loopLabelVisitor = {
|
---|
220 | LabeledStatement({
|
---|
221 | node
|
---|
222 | }, state) {
|
---|
223 | state.innerLabels.push(node.label.name);
|
---|
224 | }
|
---|
225 |
|
---|
226 | };
|
---|
227 | const continuationVisitor = {
|
---|
228 | enter(path, state) {
|
---|
229 | if (path.isAssignmentExpression() || path.isUpdateExpression()) {
|
---|
230 | for (const name of Object.keys(path.getBindingIdentifiers())) {
|
---|
231 | if (state.outsideReferences.get(name) !== path.scope.getBindingIdentifier(name)) {
|
---|
232 | continue;
|
---|
233 | }
|
---|
234 |
|
---|
235 | state.reassignments[name] = true;
|
---|
236 | }
|
---|
237 | } else if (path.isReturnStatement()) {
|
---|
238 | state.returnStatements.push(path);
|
---|
239 | }
|
---|
240 | }
|
---|
241 |
|
---|
242 | };
|
---|
243 |
|
---|
244 | function loopNodeTo(node) {
|
---|
245 | if (_core.types.isBreakStatement(node)) {
|
---|
246 | return "break";
|
---|
247 | } else if (_core.types.isContinueStatement(node)) {
|
---|
248 | return "continue";
|
---|
249 | }
|
---|
250 | }
|
---|
251 |
|
---|
252 | const loopVisitor = {
|
---|
253 | Loop(path, state) {
|
---|
254 | const oldIgnoreLabeless = state.ignoreLabeless;
|
---|
255 | state.ignoreLabeless = true;
|
---|
256 | path.traverse(loopVisitor, state);
|
---|
257 | state.ignoreLabeless = oldIgnoreLabeless;
|
---|
258 | path.skip();
|
---|
259 | },
|
---|
260 |
|
---|
261 | Function(path) {
|
---|
262 | path.skip();
|
---|
263 | },
|
---|
264 |
|
---|
265 | SwitchCase(path, state) {
|
---|
266 | const oldInSwitchCase = state.inSwitchCase;
|
---|
267 | state.inSwitchCase = true;
|
---|
268 | path.traverse(loopVisitor, state);
|
---|
269 | state.inSwitchCase = oldInSwitchCase;
|
---|
270 | path.skip();
|
---|
271 | },
|
---|
272 |
|
---|
273 | "BreakStatement|ContinueStatement|ReturnStatement"(path, state) {
|
---|
274 | const {
|
---|
275 | node,
|
---|
276 | scope
|
---|
277 | } = path;
|
---|
278 | if (node[this.LOOP_IGNORE]) return;
|
---|
279 | let replace;
|
---|
280 | let loopText = loopNodeTo(node);
|
---|
281 |
|
---|
282 | if (loopText) {
|
---|
283 | if (_core.types.isReturnStatement(node)) {
|
---|
284 | throw new Error("Internal error: unexpected return statement with `loopText`");
|
---|
285 | }
|
---|
286 |
|
---|
287 | if (node.label) {
|
---|
288 | if (state.innerLabels.indexOf(node.label.name) >= 0) {
|
---|
289 | return;
|
---|
290 | }
|
---|
291 |
|
---|
292 | loopText = `${loopText}|${node.label.name}`;
|
---|
293 | } else {
|
---|
294 | if (state.ignoreLabeless) return;
|
---|
295 | if (_core.types.isBreakStatement(node) && state.inSwitchCase) return;
|
---|
296 | }
|
---|
297 |
|
---|
298 | state.hasBreakContinue = true;
|
---|
299 | state.map[loopText] = node;
|
---|
300 | replace = _core.types.stringLiteral(loopText);
|
---|
301 | }
|
---|
302 |
|
---|
303 | if (_core.types.isReturnStatement(node)) {
|
---|
304 | state.hasReturn = true;
|
---|
305 | replace = _core.types.objectExpression([_core.types.objectProperty(_core.types.identifier("v"), node.argument || scope.buildUndefinedNode())]);
|
---|
306 | }
|
---|
307 |
|
---|
308 | if (replace) {
|
---|
309 | replace = _core.types.returnStatement(replace);
|
---|
310 | replace[this.LOOP_IGNORE] = true;
|
---|
311 | path.skip();
|
---|
312 | path.replaceWith(_core.types.inherits(replace, node));
|
---|
313 | }
|
---|
314 | }
|
---|
315 |
|
---|
316 | };
|
---|
317 |
|
---|
318 | function isStrict(path) {
|
---|
319 | return !!path.find(({
|
---|
320 | node
|
---|
321 | }) => {
|
---|
322 | if (_core.types.isProgram(node)) {
|
---|
323 | if (node.sourceType === "module") return true;
|
---|
324 | } else if (!_core.types.isBlockStatement(node)) return false;
|
---|
325 |
|
---|
326 | return node.directives.some(directive => directive.value.value === "use strict");
|
---|
327 | });
|
---|
328 | }
|
---|
329 |
|
---|
330 | class BlockScoping {
|
---|
331 | constructor(loopPath, blockPath, parent, scope, throwIfClosureRequired, tdzEnabled, state) {
|
---|
332 | this.parent = void 0;
|
---|
333 | this.state = void 0;
|
---|
334 | this.scope = void 0;
|
---|
335 | this.throwIfClosureRequired = void 0;
|
---|
336 | this.tdzEnabled = void 0;
|
---|
337 | this.blockPath = void 0;
|
---|
338 | this.block = void 0;
|
---|
339 | this.outsideLetReferences = void 0;
|
---|
340 | this.hasLetReferences = void 0;
|
---|
341 | this.letReferences = void 0;
|
---|
342 | this.body = void 0;
|
---|
343 | this.loopParent = void 0;
|
---|
344 | this.loopLabel = void 0;
|
---|
345 | this.loopPath = void 0;
|
---|
346 | this.loop = void 0;
|
---|
347 | this.has = void 0;
|
---|
348 | this.parent = parent;
|
---|
349 | this.scope = scope;
|
---|
350 | this.state = state;
|
---|
351 | this.throwIfClosureRequired = throwIfClosureRequired;
|
---|
352 | this.tdzEnabled = tdzEnabled;
|
---|
353 | this.blockPath = blockPath;
|
---|
354 | this.block = blockPath.node;
|
---|
355 | this.outsideLetReferences = new Map();
|
---|
356 | this.hasLetReferences = false;
|
---|
357 | this.letReferences = new Map();
|
---|
358 | this.body = [];
|
---|
359 |
|
---|
360 | if (loopPath) {
|
---|
361 | this.loopParent = loopPath.parent;
|
---|
362 | this.loopLabel = _core.types.isLabeledStatement(this.loopParent) && this.loopParent.label;
|
---|
363 | this.loopPath = loopPath;
|
---|
364 | this.loop = loopPath.node;
|
---|
365 | }
|
---|
366 | }
|
---|
367 |
|
---|
368 | run() {
|
---|
369 | const block = this.block;
|
---|
370 | if (DONE.has(block)) return;
|
---|
371 | DONE.add(block);
|
---|
372 | const needsClosure = this.getLetReferences();
|
---|
373 | this.checkConstants();
|
---|
374 |
|
---|
375 | if (_core.types.isFunction(this.parent) || _core.types.isProgram(this.block)) {
|
---|
376 | this.updateScopeInfo();
|
---|
377 | return;
|
---|
378 | }
|
---|
379 |
|
---|
380 | if (!this.hasLetReferences) return;
|
---|
381 |
|
---|
382 | if (needsClosure) {
|
---|
383 | this.wrapClosure();
|
---|
384 | } else {
|
---|
385 | this.remap();
|
---|
386 | }
|
---|
387 |
|
---|
388 | this.updateScopeInfo(needsClosure);
|
---|
389 |
|
---|
390 | if (this.loopLabel && !_core.types.isLabeledStatement(this.loopParent)) {
|
---|
391 | return _core.types.labeledStatement(this.loopLabel, this.loop);
|
---|
392 | }
|
---|
393 | }
|
---|
394 |
|
---|
395 | checkConstants() {
|
---|
396 | const scope = this.scope;
|
---|
397 | const state = this.state;
|
---|
398 |
|
---|
399 | for (const name of Object.keys(scope.bindings)) {
|
---|
400 | const binding = scope.bindings[name];
|
---|
401 | if (binding.kind !== "const") continue;
|
---|
402 |
|
---|
403 | for (const violation of binding.constantViolations) {
|
---|
404 | const readOnlyError = state.addHelper("readOnlyError");
|
---|
405 |
|
---|
406 | const throwNode = _core.types.callExpression(readOnlyError, [_core.types.stringLiteral(name)]);
|
---|
407 |
|
---|
408 | if (violation.isAssignmentExpression()) {
|
---|
409 | const {
|
---|
410 | operator
|
---|
411 | } = violation.node;
|
---|
412 |
|
---|
413 | if (operator === "=") {
|
---|
414 | violation.replaceWith(_core.types.sequenceExpression([violation.get("right").node, throwNode]));
|
---|
415 | } else if (["&&=", "||=", "??="].includes(operator)) {
|
---|
416 | violation.replaceWith(_core.types.logicalExpression(operator.slice(0, -1), violation.get("left").node, _core.types.sequenceExpression([violation.get("right").node, throwNode])));
|
---|
417 | } else {
|
---|
418 | violation.replaceWith(_core.types.sequenceExpression([_core.types.binaryExpression(operator.slice(0, -1), violation.get("left").node, violation.get("right").node), throwNode]));
|
---|
419 | }
|
---|
420 | } else if (violation.isUpdateExpression()) {
|
---|
421 | violation.replaceWith(_core.types.sequenceExpression([_core.types.unaryExpression("+", violation.get("argument").node), throwNode]));
|
---|
422 | } else if (violation.isForXStatement()) {
|
---|
423 | violation.ensureBlock();
|
---|
424 | violation.get("left").replaceWith(_core.types.variableDeclaration("var", [_core.types.variableDeclarator(violation.scope.generateUidIdentifier(name))]));
|
---|
425 | violation.node.body.body.unshift(_core.types.expressionStatement(throwNode));
|
---|
426 | }
|
---|
427 | }
|
---|
428 | }
|
---|
429 | }
|
---|
430 |
|
---|
431 | updateScopeInfo(wrappedInClosure) {
|
---|
432 | const blockScope = this.blockPath.scope;
|
---|
433 | const parentScope = blockScope.getFunctionParent() || blockScope.getProgramParent();
|
---|
434 | const letRefs = this.letReferences;
|
---|
435 |
|
---|
436 | for (const key of letRefs.keys()) {
|
---|
437 | const ref = letRefs.get(key);
|
---|
438 | const binding = blockScope.getBinding(ref.name);
|
---|
439 | if (!binding) continue;
|
---|
440 |
|
---|
441 | if (binding.kind === "let" || binding.kind === "const") {
|
---|
442 | binding.kind = "var";
|
---|
443 |
|
---|
444 | if (wrappedInClosure) {
|
---|
445 | if (blockScope.hasOwnBinding(ref.name)) {
|
---|
446 | blockScope.removeBinding(ref.name);
|
---|
447 | }
|
---|
448 | } else {
|
---|
449 | blockScope.moveBindingTo(ref.name, parentScope);
|
---|
450 | }
|
---|
451 | }
|
---|
452 | }
|
---|
453 | }
|
---|
454 |
|
---|
455 | remap() {
|
---|
456 | const letRefs = this.letReferences;
|
---|
457 | const outsideLetRefs = this.outsideLetReferences;
|
---|
458 | const scope = this.scope;
|
---|
459 | const blockPathScope = this.blockPath.scope;
|
---|
460 |
|
---|
461 | for (const key of letRefs.keys()) {
|
---|
462 | const ref = letRefs.get(key);
|
---|
463 |
|
---|
464 | if (scope.parentHasBinding(key) || scope.hasGlobal(key)) {
|
---|
465 | const binding = scope.getOwnBinding(key);
|
---|
466 |
|
---|
467 | if (binding) {
|
---|
468 | const parentBinding = scope.parent.getOwnBinding(key);
|
---|
469 |
|
---|
470 | if (binding.kind === "hoisted" && !binding.path.node.async && !binding.path.node.generator && (!parentBinding || isVar(parentBinding.path.parent)) && !isStrict(binding.path.parentPath)) {
|
---|
471 | continue;
|
---|
472 | }
|
---|
473 |
|
---|
474 | scope.rename(ref.name);
|
---|
475 | }
|
---|
476 |
|
---|
477 | if (blockPathScope.hasOwnBinding(key)) {
|
---|
478 | blockPathScope.rename(ref.name);
|
---|
479 | }
|
---|
480 | }
|
---|
481 | }
|
---|
482 |
|
---|
483 | for (const key of outsideLetRefs.keys()) {
|
---|
484 | const ref = letRefs.get(key);
|
---|
485 |
|
---|
486 | if (isInLoop(this.blockPath) && blockPathScope.hasOwnBinding(key)) {
|
---|
487 | blockPathScope.rename(ref.name);
|
---|
488 | }
|
---|
489 | }
|
---|
490 | }
|
---|
491 |
|
---|
492 | wrapClosure() {
|
---|
493 | if (this.throwIfClosureRequired) {
|
---|
494 | throw this.blockPath.buildCodeFrameError("Compiling let/const in this block would add a closure " + "(throwIfClosureRequired).");
|
---|
495 | }
|
---|
496 |
|
---|
497 | const block = this.block;
|
---|
498 | const outsideRefs = this.outsideLetReferences;
|
---|
499 |
|
---|
500 | if (this.loop) {
|
---|
501 | for (const name of Array.from(outsideRefs.keys())) {
|
---|
502 | const id = outsideRefs.get(name);
|
---|
503 |
|
---|
504 | if (this.scope.hasGlobal(id.name) || this.scope.parentHasBinding(id.name)) {
|
---|
505 | outsideRefs.delete(id.name);
|
---|
506 | this.letReferences.delete(id.name);
|
---|
507 | this.scope.rename(id.name);
|
---|
508 | this.letReferences.set(id.name, id);
|
---|
509 | outsideRefs.set(id.name, id);
|
---|
510 | }
|
---|
511 | }
|
---|
512 | }
|
---|
513 |
|
---|
514 | this.has = this.checkLoop();
|
---|
515 | this.hoistVarDeclarations();
|
---|
516 | const args = Array.from(outsideRefs.values(), node => _core.types.cloneNode(node));
|
---|
517 | const params = args.map(id => _core.types.cloneNode(id));
|
---|
518 | const isSwitch = this.blockPath.isSwitchStatement();
|
---|
519 |
|
---|
520 | const fn = _core.types.functionExpression(null, params, _core.types.blockStatement(isSwitch ? [block] : block.body));
|
---|
521 |
|
---|
522 | this.addContinuations(fn);
|
---|
523 |
|
---|
524 | let call = _core.types.callExpression(_core.types.nullLiteral(), args);
|
---|
525 |
|
---|
526 | let basePath = ".callee";
|
---|
527 |
|
---|
528 | const hasYield = _core.traverse.hasType(fn.body, "YieldExpression", _core.types.FUNCTION_TYPES);
|
---|
529 |
|
---|
530 | if (hasYield) {
|
---|
531 | fn.generator = true;
|
---|
532 | call = _core.types.yieldExpression(call, true);
|
---|
533 | basePath = ".argument" + basePath;
|
---|
534 | }
|
---|
535 |
|
---|
536 | const hasAsync = _core.traverse.hasType(fn.body, "AwaitExpression", _core.types.FUNCTION_TYPES);
|
---|
537 |
|
---|
538 | if (hasAsync) {
|
---|
539 | fn.async = true;
|
---|
540 | call = _core.types.awaitExpression(call);
|
---|
541 | basePath = ".argument" + basePath;
|
---|
542 | }
|
---|
543 |
|
---|
544 | let placeholderPath;
|
---|
545 | let index;
|
---|
546 |
|
---|
547 | if (this.has.hasReturn || this.has.hasBreakContinue) {
|
---|
548 | const ret = this.scope.generateUid("ret");
|
---|
549 | this.body.push(_core.types.variableDeclaration("var", [_core.types.variableDeclarator(_core.types.identifier(ret), call)]));
|
---|
550 | placeholderPath = "declarations.0.init" + basePath;
|
---|
551 | index = this.body.length - 1;
|
---|
552 | this.buildHas(ret);
|
---|
553 | } else {
|
---|
554 | this.body.push(_core.types.expressionStatement(call));
|
---|
555 | placeholderPath = "expression" + basePath;
|
---|
556 | index = this.body.length - 1;
|
---|
557 | }
|
---|
558 |
|
---|
559 | let callPath;
|
---|
560 |
|
---|
561 | if (isSwitch) {
|
---|
562 | const {
|
---|
563 | parentPath,
|
---|
564 | listKey,
|
---|
565 | key
|
---|
566 | } = this.blockPath;
|
---|
567 | this.blockPath.replaceWithMultiple(this.body);
|
---|
568 | callPath = parentPath.get(listKey)[key + index];
|
---|
569 | } else {
|
---|
570 | block.body = this.body;
|
---|
571 | callPath = this.blockPath.get("body")[index];
|
---|
572 | }
|
---|
573 |
|
---|
574 | const placeholder = callPath.get(placeholderPath);
|
---|
575 | let fnPath;
|
---|
576 |
|
---|
577 | if (this.loop) {
|
---|
578 | const loopId = this.scope.generateUid("loop");
|
---|
579 | const p = this.loopPath.insertBefore(_core.types.variableDeclaration("var", [_core.types.variableDeclarator(_core.types.identifier(loopId), fn)]));
|
---|
580 | placeholder.replaceWith(_core.types.identifier(loopId));
|
---|
581 | fnPath = p[0].get("declarations.0.init");
|
---|
582 | } else {
|
---|
583 | placeholder.replaceWith(fn);
|
---|
584 | fnPath = placeholder;
|
---|
585 | }
|
---|
586 |
|
---|
587 | fnPath.unwrapFunctionEnvironment();
|
---|
588 | }
|
---|
589 |
|
---|
590 | addContinuations(fn) {
|
---|
591 | const state = {
|
---|
592 | reassignments: {},
|
---|
593 | returnStatements: [],
|
---|
594 | outsideReferences: this.outsideLetReferences
|
---|
595 | };
|
---|
596 | this.scope.traverse(fn, continuationVisitor, state);
|
---|
597 |
|
---|
598 | for (let i = 0; i < fn.params.length; i++) {
|
---|
599 | const param = fn.params[i];
|
---|
600 | if (!state.reassignments[param.name]) continue;
|
---|
601 | const paramName = param.name;
|
---|
602 | const newParamName = this.scope.generateUid(param.name);
|
---|
603 | fn.params[i] = _core.types.identifier(newParamName);
|
---|
604 | this.scope.rename(paramName, newParamName, fn);
|
---|
605 | state.returnStatements.forEach(returnStatement => {
|
---|
606 | returnStatement.insertBefore(_core.types.expressionStatement(_core.types.assignmentExpression("=", _core.types.identifier(paramName), _core.types.identifier(newParamName))));
|
---|
607 | });
|
---|
608 | fn.body.body.push(_core.types.expressionStatement(_core.types.assignmentExpression("=", _core.types.identifier(paramName), _core.types.identifier(newParamName))));
|
---|
609 | }
|
---|
610 | }
|
---|
611 |
|
---|
612 | getLetReferences() {
|
---|
613 | const block = this.block;
|
---|
614 | const declarators = [];
|
---|
615 |
|
---|
616 | if (this.loop) {
|
---|
617 | const init = this.loop.left || this.loop.init;
|
---|
618 |
|
---|
619 | if (isBlockScoped(init)) {
|
---|
620 | declarators.push(init);
|
---|
621 |
|
---|
622 | const names = _core.types.getBindingIdentifiers(init);
|
---|
623 |
|
---|
624 | for (const name of Object.keys(names)) {
|
---|
625 | this.outsideLetReferences.set(name, names[name]);
|
---|
626 | }
|
---|
627 | }
|
---|
628 | }
|
---|
629 |
|
---|
630 | const addDeclarationsFromChild = (path, node) => {
|
---|
631 | node = node || path.node;
|
---|
632 |
|
---|
633 | if (_core.types.isClassDeclaration(node) || _core.types.isFunctionDeclaration(node) || isBlockScoped(node)) {
|
---|
634 | if (isBlockScoped(node)) {
|
---|
635 | convertBlockScopedToVar(path, node, block, this.scope);
|
---|
636 | }
|
---|
637 |
|
---|
638 | if (node.declarations) {
|
---|
639 | for (let i = 0; i < node.declarations.length; i++) {
|
---|
640 | declarators.push(node.declarations[i]);
|
---|
641 | }
|
---|
642 | } else {
|
---|
643 | declarators.push(node);
|
---|
644 | }
|
---|
645 | }
|
---|
646 |
|
---|
647 | if (_core.types.isLabeledStatement(node)) {
|
---|
648 | addDeclarationsFromChild(path.get("body"), node.body);
|
---|
649 | }
|
---|
650 | };
|
---|
651 |
|
---|
652 | if (block.body) {
|
---|
653 | const declarPaths = this.blockPath.get("body");
|
---|
654 |
|
---|
655 | for (let i = 0; i < block.body.length; i++) {
|
---|
656 | addDeclarationsFromChild(declarPaths[i]);
|
---|
657 | }
|
---|
658 | }
|
---|
659 |
|
---|
660 | if (block.cases) {
|
---|
661 | const declarPaths = this.blockPath.get("cases");
|
---|
662 |
|
---|
663 | for (let i = 0; i < block.cases.length; i++) {
|
---|
664 | const consequents = block.cases[i].consequent;
|
---|
665 |
|
---|
666 | for (let j = 0; j < consequents.length; j++) {
|
---|
667 | const declar = consequents[j];
|
---|
668 | addDeclarationsFromChild(declarPaths[i], declar);
|
---|
669 | }
|
---|
670 | }
|
---|
671 | }
|
---|
672 |
|
---|
673 | for (let i = 0; i < declarators.length; i++) {
|
---|
674 | const declar = declarators[i];
|
---|
675 |
|
---|
676 | const keys = _core.types.getBindingIdentifiers(declar, false, true);
|
---|
677 |
|
---|
678 | for (const key of Object.keys(keys)) {
|
---|
679 | this.letReferences.set(key, keys[key]);
|
---|
680 | }
|
---|
681 |
|
---|
682 | this.hasLetReferences = true;
|
---|
683 | }
|
---|
684 |
|
---|
685 | if (!this.hasLetReferences) return;
|
---|
686 | const state = {
|
---|
687 | letReferences: this.letReferences,
|
---|
688 | closurify: false,
|
---|
689 | loopDepth: 0,
|
---|
690 | tdzEnabled: this.tdzEnabled,
|
---|
691 | addHelper: name => this.state.addHelper(name)
|
---|
692 | };
|
---|
693 |
|
---|
694 | if (isInLoop(this.blockPath)) {
|
---|
695 | state.loopDepth++;
|
---|
696 | }
|
---|
697 |
|
---|
698 | this.blockPath.traverse(letReferenceBlockVisitor, state);
|
---|
699 | return state.closurify;
|
---|
700 | }
|
---|
701 |
|
---|
702 | checkLoop() {
|
---|
703 | const state = {
|
---|
704 | hasBreakContinue: false,
|
---|
705 | ignoreLabeless: false,
|
---|
706 | inSwitchCase: false,
|
---|
707 | innerLabels: [],
|
---|
708 | hasReturn: false,
|
---|
709 | isLoop: !!this.loop,
|
---|
710 | map: {},
|
---|
711 | LOOP_IGNORE: Symbol()
|
---|
712 | };
|
---|
713 | this.blockPath.traverse(loopLabelVisitor, state);
|
---|
714 | this.blockPath.traverse(loopVisitor, state);
|
---|
715 | return state;
|
---|
716 | }
|
---|
717 |
|
---|
718 | hoistVarDeclarations() {
|
---|
719 | this.blockPath.traverse(hoistVarDeclarationsVisitor, this);
|
---|
720 | }
|
---|
721 |
|
---|
722 | pushDeclar(node) {
|
---|
723 | const declars = [];
|
---|
724 |
|
---|
725 | const names = _core.types.getBindingIdentifiers(node);
|
---|
726 |
|
---|
727 | for (const name of Object.keys(names)) {
|
---|
728 | declars.push(_core.types.variableDeclarator(names[name]));
|
---|
729 | }
|
---|
730 |
|
---|
731 | this.body.push(_core.types.variableDeclaration(node.kind, declars));
|
---|
732 | const replace = [];
|
---|
733 |
|
---|
734 | for (let i = 0; i < node.declarations.length; i++) {
|
---|
735 | const declar = node.declarations[i];
|
---|
736 | if (!declar.init) continue;
|
---|
737 |
|
---|
738 | const expr = _core.types.assignmentExpression("=", _core.types.cloneNode(declar.id), _core.types.cloneNode(declar.init));
|
---|
739 |
|
---|
740 | replace.push(_core.types.inherits(expr, declar));
|
---|
741 | }
|
---|
742 |
|
---|
743 | return replace;
|
---|
744 | }
|
---|
745 |
|
---|
746 | buildHas(ret) {
|
---|
747 | const body = this.body;
|
---|
748 | const has = this.has;
|
---|
749 |
|
---|
750 | if (has.hasBreakContinue) {
|
---|
751 | for (const key of Object.keys(has.map)) {
|
---|
752 | body.push(_core.types.ifStatement(_core.types.binaryExpression("===", _core.types.identifier(ret), _core.types.stringLiteral(key)), has.map[key]));
|
---|
753 | }
|
---|
754 | }
|
---|
755 |
|
---|
756 | if (has.hasReturn) {
|
---|
757 | body.push(buildRetCheck({
|
---|
758 | RETURN: _core.types.identifier(ret)
|
---|
759 | }));
|
---|
760 | }
|
---|
761 | }
|
---|
762 |
|
---|
763 | } |
---|