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