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 | const {
|
189 | node,
|
190 | parent
|
191 | } = path;
|
192 |
|
193 | if (path.isForStatement()) {
|
194 | if (isVar(node.init, node)) {
|
195 | const nodes = self.pushDeclar(node.init);
|
196 |
|
197 | if (nodes.length === 1) {
|
198 | node.init = nodes[0];
|
199 | } else {
|
200 | node.init = _core.types.sequenceExpression(nodes);
|
201 | }
|
202 | }
|
203 | } else if (path.isFor()) {
|
204 | if (isVar(node.left, node)) {
|
205 | self.pushDeclar(node.left);
|
206 | node.left = node.left.declarations[0].id;
|
207 | }
|
208 | } else if (isVar(node, parent)) {
|
209 | path.replaceWithMultiple(self.pushDeclar(node).map(expr => _core.types.expressionStatement(expr)));
|
210 | } else if (path.isFunction()) {
|
211 | return path.skip();
|
212 | }
|
213 | }
|
214 |
|
215 | };
|
216 | const loopLabelVisitor = {
|
217 | LabeledStatement({
|
218 | node
|
219 | }, state) {
|
220 | state.innerLabels.push(node.label.name);
|
221 | }
|
222 |
|
223 | };
|
224 | const continuationVisitor = {
|
225 | enter(path, state) {
|
226 | if (path.isAssignmentExpression() || path.isUpdateExpression()) {
|
227 | for (const name of Object.keys(path.getBindingIdentifiers())) {
|
228 | if (state.outsideReferences.get(name) !== path.scope.getBindingIdentifier(name)) {
|
229 | continue;
|
230 | }
|
231 |
|
232 | state.reassignments[name] = true;
|
233 | }
|
234 | } else if (path.isReturnStatement()) {
|
235 | state.returnStatements.push(path);
|
236 | }
|
237 | }
|
238 |
|
239 | };
|
240 |
|
241 | function loopNodeTo(node) {
|
242 | if (_core.types.isBreakStatement(node)) {
|
243 | return "break";
|
244 | } else if (_core.types.isContinueStatement(node)) {
|
245 | return "continue";
|
246 | }
|
247 | }
|
248 |
|
249 | const loopVisitor = {
|
250 | Loop(path, state) {
|
251 | const oldIgnoreLabeless = state.ignoreLabeless;
|
252 | state.ignoreLabeless = true;
|
253 | path.traverse(loopVisitor, state);
|
254 | state.ignoreLabeless = oldIgnoreLabeless;
|
255 | path.skip();
|
256 | },
|
257 |
|
258 | Function(path) {
|
259 | path.skip();
|
260 | },
|
261 |
|
262 | SwitchCase(path, state) {
|
263 | const oldInSwitchCase = state.inSwitchCase;
|
264 | state.inSwitchCase = true;
|
265 | path.traverse(loopVisitor, state);
|
266 | state.inSwitchCase = oldInSwitchCase;
|
267 | path.skip();
|
268 | },
|
269 |
|
270 | "BreakStatement|ContinueStatement|ReturnStatement"(path, state) {
|
271 | const {
|
272 | node,
|
273 | scope
|
274 | } = path;
|
275 | if (node[this.LOOP_IGNORE]) return;
|
276 | let replace;
|
277 | let loopText = loopNodeTo(node);
|
278 |
|
279 | if (loopText) {
|
280 | if (node.label) {
|
281 | if (state.innerLabels.indexOf(node.label.name) >= 0) {
|
282 | return;
|
283 | }
|
284 |
|
285 | loopText = `${loopText}|${node.label.name}`;
|
286 | } else {
|
287 | if (state.ignoreLabeless) return;
|
288 | if (_core.types.isBreakStatement(node) && state.inSwitchCase) return;
|
289 | }
|
290 |
|
291 | state.hasBreakContinue = true;
|
292 | state.map[loopText] = node;
|
293 | replace = _core.types.stringLiteral(loopText);
|
294 | }
|
295 |
|
296 | if (path.isReturnStatement()) {
|
297 | state.hasReturn = true;
|
298 | replace = _core.types.objectExpression([_core.types.objectProperty(_core.types.identifier("v"), node.argument || scope.buildUndefinedNode())]);
|
299 | }
|
300 |
|
301 | if (replace) {
|
302 | replace = _core.types.returnStatement(replace);
|
303 | replace[this.LOOP_IGNORE] = true;
|
304 | path.skip();
|
305 | path.replaceWith(_core.types.inherits(replace, node));
|
306 | }
|
307 | }
|
308 |
|
309 | };
|
310 |
|
311 | function isStrict(path) {
|
312 | return !!path.find(({
|
313 | node
|
314 | }) => {
|
315 | if (_core.types.isProgram(node)) {
|
316 | if (node.sourceType === "module") return true;
|
317 | } else if (!_core.types.isBlockStatement(node)) return false;
|
318 |
|
319 | return node.directives.some(directive => directive.value.value === "use strict");
|
320 | });
|
321 | }
|
322 |
|
323 | class BlockScoping {
|
324 | constructor(loopPath, blockPath, parent, scope, throwIfClosureRequired, tdzEnabled, state) {
|
325 | this.parent = parent;
|
326 | this.scope = scope;
|
327 | this.state = state;
|
328 | this.throwIfClosureRequired = throwIfClosureRequired;
|
329 | this.tdzEnabled = tdzEnabled;
|
330 | this.blockPath = blockPath;
|
331 | this.block = blockPath.node;
|
332 | this.outsideLetReferences = new Map();
|
333 | this.hasLetReferences = false;
|
334 | this.letReferences = new Map();
|
335 | this.body = [];
|
336 |
|
337 | if (loopPath) {
|
338 | this.loopParent = loopPath.parent;
|
339 | this.loopLabel = _core.types.isLabeledStatement(this.loopParent) && this.loopParent.label;
|
340 | this.loopPath = loopPath;
|
341 | this.loop = loopPath.node;
|
342 | }
|
343 | }
|
344 |
|
345 | run() {
|
346 | const block = this.block;
|
347 | if (DONE.has(block)) return;
|
348 | DONE.add(block);
|
349 | const needsClosure = this.getLetReferences();
|
350 | this.checkConstants();
|
351 |
|
352 | if (_core.types.isFunction(this.parent) || _core.types.isProgram(this.block)) {
|
353 | this.updateScopeInfo();
|
354 | return;
|
355 | }
|
356 |
|
357 | if (!this.hasLetReferences) return;
|
358 |
|
359 | if (needsClosure) {
|
360 | this.wrapClosure();
|
361 | } else {
|
362 | this.remap();
|
363 | }
|
364 |
|
365 | this.updateScopeInfo(needsClosure);
|
366 |
|
367 | if (this.loopLabel && !_core.types.isLabeledStatement(this.loopParent)) {
|
368 | return _core.types.labeledStatement(this.loopLabel, this.loop);
|
369 | }
|
370 | }
|
371 |
|
372 | checkConstants() {
|
373 | const scope = this.scope;
|
374 | const state = this.state;
|
375 |
|
376 | for (const name of Object.keys(scope.bindings)) {
|
377 | const binding = scope.bindings[name];
|
378 | if (binding.kind !== "const") continue;
|
379 |
|
380 | for (const violation of binding.constantViolations) {
|
381 | const readOnlyError = state.addHelper("readOnlyError");
|
382 |
|
383 | const throwNode = _core.types.callExpression(readOnlyError, [_core.types.stringLiteral(name)]);
|
384 |
|
385 | if (violation.isAssignmentExpression()) {
|
386 | const {
|
387 | operator
|
388 | } = violation.node;
|
389 |
|
390 | if (operator === "=") {
|
391 | violation.replaceWith(_core.types.sequenceExpression([violation.get("right").node, throwNode]));
|
392 | } else if (["&&=", "||=", "??="].includes(operator)) {
|
393 | violation.replaceWith(_core.types.logicalExpression(operator.slice(0, -1), violation.get("left").node, _core.types.sequenceExpression([violation.get("right").node, throwNode])));
|
394 | } else {
|
395 | violation.replaceWith(_core.types.sequenceExpression([_core.types.binaryExpression(operator.slice(0, -1), violation.get("left").node, violation.get("right").node), throwNode]));
|
396 | }
|
397 | } else if (violation.isUpdateExpression()) {
|
398 | violation.replaceWith(_core.types.sequenceExpression([_core.types.unaryExpression("+", violation.get("argument").node), throwNode]));
|
399 | } else if (violation.isForXStatement()) {
|
400 | violation.ensureBlock();
|
401 | violation.get("left").replaceWith(_core.types.variableDeclaration("var", [_core.types.variableDeclarator(violation.scope.generateUidIdentifier(name))]));
|
402 | violation.node.body.body.unshift(_core.types.expressionStatement(throwNode));
|
403 | }
|
404 | }
|
405 | }
|
406 | }
|
407 |
|
408 | updateScopeInfo(wrappedInClosure) {
|
409 | const blockScope = this.blockPath.scope;
|
410 | const parentScope = blockScope.getFunctionParent() || blockScope.getProgramParent();
|
411 | const letRefs = this.letReferences;
|
412 |
|
413 | for (const key of letRefs.keys()) {
|
414 | const ref = letRefs.get(key);
|
415 | const binding = blockScope.getBinding(ref.name);
|
416 | if (!binding) continue;
|
417 |
|
418 | if (binding.kind === "let" || binding.kind === "const") {
|
419 | binding.kind = "var";
|
420 |
|
421 | if (wrappedInClosure) {
|
422 | if (blockScope.hasOwnBinding(ref.name)) {
|
423 | blockScope.removeBinding(ref.name);
|
424 | }
|
425 | } else {
|
426 | blockScope.moveBindingTo(ref.name, parentScope);
|
427 | }
|
428 | }
|
429 | }
|
430 | }
|
431 |
|
432 | remap() {
|
433 | const letRefs = this.letReferences;
|
434 | const outsideLetRefs = this.outsideLetReferences;
|
435 | const scope = this.scope;
|
436 | const blockPathScope = this.blockPath.scope;
|
437 |
|
438 | for (const key of letRefs.keys()) {
|
439 | const ref = letRefs.get(key);
|
440 |
|
441 | if (scope.parentHasBinding(key) || scope.hasGlobal(key)) {
|
442 | const binding = scope.getOwnBinding(key);
|
443 |
|
444 | if (binding) {
|
445 | const parentBinding = scope.parent.getOwnBinding(key);
|
446 |
|
447 | if (binding.kind === "hoisted" && !binding.path.node.async && !binding.path.node.generator && (!parentBinding || isVar(parentBinding.path.parent)) && !isStrict(binding.path.parentPath)) {
|
448 | continue;
|
449 | }
|
450 |
|
451 | scope.rename(ref.name);
|
452 | }
|
453 |
|
454 | if (blockPathScope.hasOwnBinding(key)) {
|
455 | blockPathScope.rename(ref.name);
|
456 | }
|
457 | }
|
458 | }
|
459 |
|
460 | for (const key of outsideLetRefs.keys()) {
|
461 | const ref = letRefs.get(key);
|
462 |
|
463 | if (isInLoop(this.blockPath) && blockPathScope.hasOwnBinding(key)) {
|
464 | blockPathScope.rename(ref.name);
|
465 | }
|
466 | }
|
467 | }
|
468 |
|
469 | wrapClosure() {
|
470 | if (this.throwIfClosureRequired) {
|
471 | throw this.blockPath.buildCodeFrameError("Compiling let/const in this block would add a closure " + "(throwIfClosureRequired).");
|
472 | }
|
473 |
|
474 | const block = this.block;
|
475 | const outsideRefs = this.outsideLetReferences;
|
476 |
|
477 | if (this.loop) {
|
478 | for (const name of Array.from(outsideRefs.keys())) {
|
479 | const id = outsideRefs.get(name);
|
480 |
|
481 | if (this.scope.hasGlobal(id.name) || this.scope.parentHasBinding(id.name)) {
|
482 | outsideRefs.delete(id.name);
|
483 | this.letReferences.delete(id.name);
|
484 | this.scope.rename(id.name);
|
485 | this.letReferences.set(id.name, id);
|
486 | outsideRefs.set(id.name, id);
|
487 | }
|
488 | }
|
489 | }
|
490 |
|
491 | this.has = this.checkLoop();
|
492 | this.hoistVarDeclarations();
|
493 | const args = Array.from(outsideRefs.values(), node => _core.types.cloneNode(node));
|
494 | const params = args.map(id => _core.types.cloneNode(id));
|
495 | const isSwitch = this.blockPath.isSwitchStatement();
|
496 |
|
497 | const fn = _core.types.functionExpression(null, params, _core.types.blockStatement(isSwitch ? [block] : block.body));
|
498 |
|
499 | this.addContinuations(fn);
|
500 |
|
501 | let call = _core.types.callExpression(_core.types.nullLiteral(), args);
|
502 |
|
503 | let basePath = ".callee";
|
504 |
|
505 | const hasYield = _core.traverse.hasType(fn.body, "YieldExpression", _core.types.FUNCTION_TYPES);
|
506 |
|
507 | if (hasYield) {
|
508 | fn.generator = true;
|
509 | call = _core.types.yieldExpression(call, true);
|
510 | basePath = ".argument" + basePath;
|
511 | }
|
512 |
|
513 | const hasAsync = _core.traverse.hasType(fn.body, "AwaitExpression", _core.types.FUNCTION_TYPES);
|
514 |
|
515 | if (hasAsync) {
|
516 | fn.async = true;
|
517 | call = _core.types.awaitExpression(call);
|
518 | basePath = ".argument" + basePath;
|
519 | }
|
520 |
|
521 | let placeholderPath;
|
522 | let index;
|
523 |
|
524 | if (this.has.hasReturn || this.has.hasBreakContinue) {
|
525 | const ret = this.scope.generateUid("ret");
|
526 | this.body.push(_core.types.variableDeclaration("var", [_core.types.variableDeclarator(_core.types.identifier(ret), call)]));
|
527 | placeholderPath = "declarations.0.init" + basePath;
|
528 | index = this.body.length - 1;
|
529 | this.buildHas(ret);
|
530 | } else {
|
531 | this.body.push(_core.types.expressionStatement(call));
|
532 | placeholderPath = "expression" + basePath;
|
533 | index = this.body.length - 1;
|
534 | }
|
535 |
|
536 | let callPath;
|
537 |
|
538 | if (isSwitch) {
|
539 | const {
|
540 | parentPath,
|
541 | listKey,
|
542 | key
|
543 | } = this.blockPath;
|
544 | this.blockPath.replaceWithMultiple(this.body);
|
545 | callPath = parentPath.get(listKey)[key + index];
|
546 | } else {
|
547 | block.body = this.body;
|
548 | callPath = this.blockPath.get("body")[index];
|
549 | }
|
550 |
|
551 | const placeholder = callPath.get(placeholderPath);
|
552 | let fnPath;
|
553 |
|
554 | if (this.loop) {
|
555 | const loopId = this.scope.generateUid("loop");
|
556 | const p = this.loopPath.insertBefore(_core.types.variableDeclaration("var", [_core.types.variableDeclarator(_core.types.identifier(loopId), fn)]));
|
557 | placeholder.replaceWith(_core.types.identifier(loopId));
|
558 | fnPath = p[0].get("declarations.0.init");
|
559 | } else {
|
560 | placeholder.replaceWith(fn);
|
561 | fnPath = placeholder;
|
562 | }
|
563 |
|
564 | fnPath.unwrapFunctionEnvironment();
|
565 | }
|
566 |
|
567 | addContinuations(fn) {
|
568 | const state = {
|
569 | reassignments: {},
|
570 | returnStatements: [],
|
571 | outsideReferences: this.outsideLetReferences
|
572 | };
|
573 | this.scope.traverse(fn, continuationVisitor, state);
|
574 |
|
575 | for (let i = 0; i < fn.params.length; i++) {
|
576 | const param = fn.params[i];
|
577 | if (!state.reassignments[param.name]) continue;
|
578 | const paramName = param.name;
|
579 | const newParamName = this.scope.generateUid(param.name);
|
580 | fn.params[i] = _core.types.identifier(newParamName);
|
581 | this.scope.rename(paramName, newParamName, fn);
|
582 | state.returnStatements.forEach(returnStatement => {
|
583 | returnStatement.insertBefore(_core.types.expressionStatement(_core.types.assignmentExpression("=", _core.types.identifier(paramName), _core.types.identifier(newParamName))));
|
584 | });
|
585 | fn.body.body.push(_core.types.expressionStatement(_core.types.assignmentExpression("=", _core.types.identifier(paramName), _core.types.identifier(newParamName))));
|
586 | }
|
587 | }
|
588 |
|
589 | getLetReferences() {
|
590 | const block = this.block;
|
591 | const declarators = [];
|
592 |
|
593 | if (this.loop) {
|
594 | const init = this.loop.left || this.loop.init;
|
595 |
|
596 | if (isBlockScoped(init)) {
|
597 | declarators.push(init);
|
598 |
|
599 | const names = _core.types.getBindingIdentifiers(init);
|
600 |
|
601 | for (const name of Object.keys(names)) {
|
602 | this.outsideLetReferences.set(name, names[name]);
|
603 | }
|
604 | }
|
605 | }
|
606 |
|
607 | const addDeclarationsFromChild = (path, node) => {
|
608 | node = node || path.node;
|
609 |
|
610 | if (_core.types.isClassDeclaration(node) || _core.types.isFunctionDeclaration(node) || isBlockScoped(node)) {
|
611 | if (isBlockScoped(node)) {
|
612 | convertBlockScopedToVar(path, node, block, this.scope);
|
613 | }
|
614 |
|
615 | if (node.declarations) {
|
616 | for (let i = 0; i < node.declarations.length; i++) {
|
617 | declarators.push(node.declarations[i]);
|
618 | }
|
619 | } else {
|
620 | declarators.push(node);
|
621 | }
|
622 | }
|
623 |
|
624 | if (_core.types.isLabeledStatement(node)) {
|
625 | addDeclarationsFromChild(path.get("body"), node.body);
|
626 | }
|
627 | };
|
628 |
|
629 | if (block.body) {
|
630 | const declarPaths = this.blockPath.get("body");
|
631 |
|
632 | for (let i = 0; i < block.body.length; i++) {
|
633 | addDeclarationsFromChild(declarPaths[i]);
|
634 | }
|
635 | }
|
636 |
|
637 | if (block.cases) {
|
638 | const declarPaths = this.blockPath.get("cases");
|
639 |
|
640 | for (let i = 0; i < block.cases.length; i++) {
|
641 | const consequents = block.cases[i].consequent;
|
642 |
|
643 | for (let j = 0; j < consequents.length; j++) {
|
644 | const declar = consequents[j];
|
645 | addDeclarationsFromChild(declarPaths[i], declar);
|
646 | }
|
647 | }
|
648 | }
|
649 |
|
650 | for (let i = 0; i < declarators.length; i++) {
|
651 | const declar = declarators[i];
|
652 |
|
653 | const keys = _core.types.getBindingIdentifiers(declar, false, true);
|
654 |
|
655 | for (const key of Object.keys(keys)) {
|
656 | this.letReferences.set(key, keys[key]);
|
657 | }
|
658 |
|
659 | this.hasLetReferences = true;
|
660 | }
|
661 |
|
662 | if (!this.hasLetReferences) return;
|
663 | const state = {
|
664 | letReferences: this.letReferences,
|
665 | closurify: false,
|
666 | loopDepth: 0,
|
667 | tdzEnabled: this.tdzEnabled,
|
668 | addHelper: name => this.state.addHelper(name)
|
669 | };
|
670 |
|
671 | if (isInLoop(this.blockPath)) {
|
672 | state.loopDepth++;
|
673 | }
|
674 |
|
675 | this.blockPath.traverse(letReferenceBlockVisitor, state);
|
676 | return state.closurify;
|
677 | }
|
678 |
|
679 | checkLoop() {
|
680 | const state = {
|
681 | hasBreakContinue: false,
|
682 | ignoreLabeless: false,
|
683 | inSwitchCase: false,
|
684 | innerLabels: [],
|
685 | hasReturn: false,
|
686 | isLoop: !!this.loop,
|
687 | map: {},
|
688 | LOOP_IGNORE: Symbol()
|
689 | };
|
690 | this.blockPath.traverse(loopLabelVisitor, state);
|
691 | this.blockPath.traverse(loopVisitor, state);
|
692 | return state;
|
693 | }
|
694 |
|
695 | hoistVarDeclarations() {
|
696 | this.blockPath.traverse(hoistVarDeclarationsVisitor, this);
|
697 | }
|
698 |
|
699 | pushDeclar(node) {
|
700 | const declars = [];
|
701 |
|
702 | const names = _core.types.getBindingIdentifiers(node);
|
703 |
|
704 | for (const name of Object.keys(names)) {
|
705 | declars.push(_core.types.variableDeclarator(names[name]));
|
706 | }
|
707 |
|
708 | this.body.push(_core.types.variableDeclaration(node.kind, declars));
|
709 | const replace = [];
|
710 |
|
711 | for (let i = 0; i < node.declarations.length; i++) {
|
712 | const declar = node.declarations[i];
|
713 | if (!declar.init) continue;
|
714 |
|
715 | const expr = _core.types.assignmentExpression("=", _core.types.cloneNode(declar.id), _core.types.cloneNode(declar.init));
|
716 |
|
717 | replace.push(_core.types.inherits(expr, declar));
|
718 | }
|
719 |
|
720 | return replace;
|
721 | }
|
722 |
|
723 | buildHas(ret) {
|
724 | const body = this.body;
|
725 | const has = this.has;
|
726 |
|
727 | if (has.hasBreakContinue) {
|
728 | for (const key of Object.keys(has.map)) {
|
729 | body.push(_core.types.ifStatement(_core.types.binaryExpression("===", _core.types.identifier(ret), _core.types.stringLiteral(key)), has.map[key]));
|
730 | }
|
731 | }
|
732 |
|
733 | if (has.hasReturn) {
|
734 | body.push(buildRetCheck({
|
735 | RETURN: _core.types.identifier(ret)
|
736 | }));
|
737 | }
|
738 | }
|
739 |
|
740 | } |
\ | No newline at end of file |