You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					194 lines
				
				6.2 KiB
			
		
		
			
		
	
	
					194 lines
				
				6.2 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								"use strict";
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Object.defineProperty(exports, "__esModule", {
							 | 
						||
| 
								 | 
							
								  value: true
							 | 
						||
| 
								 | 
							
								});
							 | 
						||
| 
								 | 
							
								exports.default = void 0;
							 | 
						||
| 
								 | 
							
								var _helperPluginUtils = require("@babel/helper-plugin-utils");
							 | 
						||
| 
								 | 
							
								var _core = require("@babel/core");
							 | 
						||
| 
								 | 
							
								var _loop = require("./loop");
							 | 
						||
| 
								 | 
							
								var _validation = require("./validation");
							 | 
						||
| 
								 | 
							
								var _annexB_3_ = require("./annex-B_3_3");
							 | 
						||
| 
								 | 
							
								var _default = (0, _helperPluginUtils.declare)((api, opts) => {
							 | 
						||
| 
								 | 
							
								  api.assertVersion(7);
							 | 
						||
| 
								 | 
							
								  const {
							 | 
						||
| 
								 | 
							
								    throwIfClosureRequired = false,
							 | 
						||
| 
								 | 
							
								    tdz: tdzEnabled = false
							 | 
						||
| 
								 | 
							
								  } = opts;
							 | 
						||
| 
								 | 
							
								  if (typeof throwIfClosureRequired !== "boolean") {
							 | 
						||
| 
								 | 
							
								    throw new Error(`.throwIfClosureRequired must be a boolean, or undefined`);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  if (typeof tdzEnabled !== "boolean") {
							 | 
						||
| 
								 | 
							
								    throw new Error(`.tdz must be a boolean, or undefined`);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return {
							 | 
						||
| 
								 | 
							
								    name: "transform-block-scoping",
							 | 
						||
| 
								 | 
							
								    visitor: _core.traverse.visitors.merge([
							 | 
						||
| 
								 | 
							
								    _annexB_3_.annexB33FunctionsVisitor, {
							 | 
						||
| 
								 | 
							
								      Loop(path, state) {
							 | 
						||
| 
								 | 
							
								        const isForStatement = path.isForStatement();
							 | 
						||
| 
								 | 
							
								        const headPath = isForStatement ? path.get("init") : path.isForXStatement() ? path.get("left") : null;
							 | 
						||
| 
								 | 
							
								        let needsBodyWrap = false;
							 | 
						||
| 
								 | 
							
								        const markNeedsBodyWrap = () => {
							 | 
						||
| 
								 | 
							
								          if (throwIfClosureRequired) {
							 | 
						||
| 
								 | 
							
								            throw path.buildCodeFrameError("Compiling let/const in this block would add a closure " + "(throwIfClosureRequired).");
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								          needsBodyWrap = true;
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								        const body = path.get("body");
							 | 
						||
| 
								 | 
							
								        let bodyScope;
							 | 
						||
| 
								 | 
							
								        if (body.isBlockStatement()) {
							 | 
						||
| 
								 | 
							
								          bodyScope = body.scope;
							 | 
						||
| 
								 | 
							
								          const bindings = (0, _loop.getLoopBodyBindings)(path);
							 | 
						||
| 
								 | 
							
								          for (const binding of bindings) {
							 | 
						||
| 
								 | 
							
								            const {
							 | 
						||
| 
								 | 
							
								              capturedInClosure
							 | 
						||
| 
								 | 
							
								            } = (0, _loop.getUsageInBody)(binding, path);
							 | 
						||
| 
								 | 
							
								            if (capturedInClosure) markNeedsBodyWrap();
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        const captured = [];
							 | 
						||
| 
								 | 
							
								        const updatedBindingsUsages = new Map();
							 | 
						||
| 
								 | 
							
								        if (headPath && isBlockScoped(headPath.node)) {
							 | 
						||
| 
								 | 
							
								          const names = Object.keys(headPath.getBindingIdentifiers());
							 | 
						||
| 
								 | 
							
								          const headScope = headPath.scope;
							 | 
						||
| 
								 | 
							
								          for (let name of names) {
							 | 
						||
| 
								 | 
							
								            var _bodyScope;
							 | 
						||
| 
								 | 
							
								            if ((_bodyScope = bodyScope) != null && _bodyScope.hasOwnBinding(name)) continue;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            let binding = headScope.getOwnBinding(name);
							 | 
						||
| 
								 | 
							
								            if (!binding) {
							 | 
						||
| 
								 | 
							
								              headScope.crawl();
							 | 
						||
| 
								 | 
							
								              binding = headScope.getOwnBinding(name);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            const {
							 | 
						||
| 
								 | 
							
								              usages,
							 | 
						||
| 
								 | 
							
								              capturedInClosure,
							 | 
						||
| 
								 | 
							
								              hasConstantViolations
							 | 
						||
| 
								 | 
							
								            } = (0, _loop.getUsageInBody)(binding, path);
							 | 
						||
| 
								 | 
							
								            if (capturedInClosure) {
							 | 
						||
| 
								 | 
							
								              markNeedsBodyWrap();
							 | 
						||
| 
								 | 
							
								              captured.push(name);
							 | 
						||
| 
								 | 
							
								            } else if (headScope.parent.hasBinding(name)) {
							 | 
						||
| 
								 | 
							
								              const newName = headScope.generateUid(name);
							 | 
						||
| 
								 | 
							
								              headPath.scope.rename(name, newName);
							 | 
						||
| 
								 | 
							
								              name = newName;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            if (isForStatement && hasConstantViolations) {
							 | 
						||
| 
								 | 
							
								              updatedBindingsUsages.set(name, usages);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (needsBodyWrap) {
							 | 
						||
| 
								 | 
							
								          const varPath = (0, _loop.wrapLoopBody)(path, captured, updatedBindingsUsages);
							 | 
						||
| 
								 | 
							
								          if (headPath != null && headPath.isVariableDeclaration()) {
							 | 
						||
| 
								 | 
							
								            transformBlockScopedVariable(headPath, state, tdzEnabled);
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								          varPath.get("declarations.0.init").unwrapFunctionEnvironment();
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      },
							 | 
						||
| 
								 | 
							
								      VariableDeclaration(path, state) {
							 | 
						||
| 
								 | 
							
								        transformBlockScopedVariable(path, state, tdzEnabled);
							 | 
						||
| 
								 | 
							
								      },
							 | 
						||
| 
								 | 
							
								      ClassDeclaration(path) {
							 | 
						||
| 
								 | 
							
								        const {
							 | 
						||
| 
								 | 
							
								          id
							 | 
						||
| 
								 | 
							
								        } = path.node;
							 | 
						||
| 
								 | 
							
								        if (!id) return;
							 | 
						||
| 
								 | 
							
								        const {
							 | 
						||
| 
								 | 
							
								          scope
							 | 
						||
| 
								 | 
							
								        } = path.parentPath;
							 | 
						||
| 
								 | 
							
								        if (!(0, _annexB_3_.isVarScope)(scope) && scope.parent.hasBinding(id.name, {
							 | 
						||
| 
								 | 
							
								          noUids: true
							 | 
						||
| 
								 | 
							
								        })) {
							 | 
						||
| 
								 | 
							
								          path.scope.rename(id.name);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }])
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								});
							 | 
						||
| 
								 | 
							
								exports.default = _default;
							 | 
						||
| 
								 | 
							
								const conflictingFunctionsVisitor = {
							 | 
						||
| 
								 | 
							
								  Scope(path, {
							 | 
						||
| 
								 | 
							
								    names
							 | 
						||
| 
								 | 
							
								  }) {
							 | 
						||
| 
								 | 
							
								    for (const name of names) {
							 | 
						||
| 
								 | 
							
								      const binding = path.scope.getOwnBinding(name);
							 | 
						||
| 
								 | 
							
								      if (binding && binding.kind === "hoisted") {
							 | 
						||
| 
								 | 
							
								        path.scope.rename(name);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  },
							 | 
						||
| 
								 | 
							
								  "Expression|Declaration"(path) {
							 | 
						||
| 
								 | 
							
								    path.skip();
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								function transformBlockScopedVariable(path, state, tdzEnabled) {
							 | 
						||
| 
								 | 
							
								  if (!isBlockScoped(path.node)) return;
							 | 
						||
| 
								 | 
							
								  const dynamicTDZNames = (0, _validation.validateUsage)(path, state, tdzEnabled);
							 | 
						||
| 
								 | 
							
								  path.node.kind = "var";
							 | 
						||
| 
								 | 
							
								  const bindingNames = Object.keys(path.getBindingIdentifiers());
							 | 
						||
| 
								 | 
							
								  for (const name of bindingNames) {
							 | 
						||
| 
								 | 
							
								    const binding = path.scope.getOwnBinding(name);
							 | 
						||
| 
								 | 
							
								    if (!binding) continue;
							 | 
						||
| 
								 | 
							
								    binding.kind = "var";
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  if (isInLoop(path) && !(0, _loop.isVarInLoopHead)(path) || dynamicTDZNames.length > 0) {
							 | 
						||
| 
								 | 
							
								    for (const decl of path.node.declarations) {
							 | 
						||
| 
								 | 
							
								      var _decl$init;
							 | 
						||
| 
								 | 
							
								      (_decl$init = decl.init) != null ? _decl$init : decl.init = path.scope.buildUndefinedNode();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  const blockScope = path.scope;
							 | 
						||
| 
								 | 
							
								  let varScope = blockScope.getFunctionParent();
							 | 
						||
| 
								 | 
							
								  let isProgramScope = false;
							 | 
						||
| 
								 | 
							
								  if (!varScope) {
							 | 
						||
| 
								 | 
							
								    varScope = blockScope.getProgramParent();
							 | 
						||
| 
								 | 
							
								    isProgramScope = true;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  if (varScope !== blockScope) {
							 | 
						||
| 
								 | 
							
								    for (const name of bindingNames) {
							 | 
						||
| 
								 | 
							
								      let newName = name;
							 | 
						||
| 
								 | 
							
								      if (
							 | 
						||
| 
								 | 
							
								      blockScope.parent.hasBinding(name, {
							 | 
						||
| 
								 | 
							
								        noUids: true
							 | 
						||
| 
								 | 
							
								      }) || blockScope.parent.hasGlobal(name) || isProgramScope && varScope.hasGlobal(name)) {
							 | 
						||
| 
								 | 
							
								        newName = blockScope.generateUid(name);
							 | 
						||
| 
								 | 
							
								        blockScope.rename(name, newName);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      blockScope.moveBindingTo(newName, varScope);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  blockScope.path.traverse(conflictingFunctionsVisitor, {
							 | 
						||
| 
								 | 
							
								    names: bindingNames
							 | 
						||
| 
								 | 
							
								  });
							 | 
						||
| 
								 | 
							
								  for (const name of dynamicTDZNames) {
							 | 
						||
| 
								 | 
							
								    path.scope.push({
							 | 
						||
| 
								 | 
							
								      id: _core.types.identifier(name),
							 | 
						||
| 
								 | 
							
								      init: state.addHelper("temporalUndefined")
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								function isLetOrConst(kind) {
							 | 
						||
| 
								 | 
							
								  return kind === "let" || kind === "const";
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								function isInLoop(path) {
							 | 
						||
| 
								 | 
							
								  if (!path.parentPath) return false;
							 | 
						||
| 
								 | 
							
								  if (path.parentPath.isLoop()) return true;
							 | 
						||
| 
								 | 
							
								  if (path.parentPath.isFunctionParent()) return false;
							 | 
						||
| 
								 | 
							
								  return isInLoop(path.parentPath);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								function isBlockScoped(node) {
							 | 
						||
| 
								 | 
							
								  if (!_core.types.isVariableDeclaration(node)) return false;
							 | 
						||
| 
								 | 
							
								  if (
							 | 
						||
| 
								 | 
							
								  node[_core.types.BLOCK_SCOPED_SYMBOL]) {
							 | 
						||
| 
								 | 
							
								    return true;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  if (!isLetOrConst(node.kind) && node.kind !== "using") {
							 | 
						||
| 
								 | 
							
								    return false;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return true;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//# sourceMappingURL=index.js.map
							 |