index.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.default = void 0;
  6. var _helperPluginUtils = require("@babel/helper-plugin-utils");
  7. var _core = require("@babel/core");
  8. var _loop = require("./loop.js");
  9. var _validation = require("./validation.js");
  10. var _annexB_3_ = require("./annex-B_3_3.js");
  11. var _default = exports.default = (0, _helperPluginUtils.declare)((api, opts) => {
  12. api.assertVersion(7);
  13. const {
  14. throwIfClosureRequired = false,
  15. tdz: tdzEnabled = false
  16. } = opts;
  17. if (typeof throwIfClosureRequired !== "boolean") {
  18. throw new Error(`.throwIfClosureRequired must be a boolean, or undefined`);
  19. }
  20. if (typeof tdzEnabled !== "boolean") {
  21. throw new Error(`.tdz must be a boolean, or undefined`);
  22. }
  23. return {
  24. name: "transform-block-scoping",
  25. visitor: _core.traverse.visitors.merge([_annexB_3_.annexB33FunctionsVisitor, {
  26. Loop(path, state) {
  27. const isForStatement = path.isForStatement();
  28. const headPath = isForStatement ? path.get("init") : path.isForXStatement() ? path.get("left") : null;
  29. let needsBodyWrap = false;
  30. const markNeedsBodyWrap = () => {
  31. if (throwIfClosureRequired) {
  32. throw path.buildCodeFrameError("Compiling let/const in this block would add a closure " + "(throwIfClosureRequired).");
  33. }
  34. needsBodyWrap = true;
  35. };
  36. const body = path.get("body");
  37. let bodyScope;
  38. if (body.isBlockStatement()) {
  39. bodyScope = body.scope;
  40. }
  41. const bindings = (0, _loop.getLoopBodyBindings)(path);
  42. for (const binding of bindings) {
  43. const {
  44. capturedInClosure
  45. } = (0, _loop.getUsageInBody)(binding, path);
  46. if (capturedInClosure) markNeedsBodyWrap();
  47. }
  48. const captured = [];
  49. const updatedBindingsUsages = new Map();
  50. if (headPath && isBlockScoped(headPath.node)) {
  51. const names = Object.keys(headPath.getBindingIdentifiers());
  52. const headScope = headPath.scope;
  53. for (let name of names) {
  54. var _bodyScope;
  55. if ((_bodyScope = bodyScope) != null && _bodyScope.hasOwnBinding(name)) continue;
  56. let binding = headScope.getOwnBinding(name);
  57. if (!binding) {
  58. headScope.crawl();
  59. binding = headScope.getOwnBinding(name);
  60. }
  61. const {
  62. usages,
  63. capturedInClosure,
  64. hasConstantViolations
  65. } = (0, _loop.getUsageInBody)(binding, path);
  66. if (headScope.parent.hasBinding(name) || headScope.parent.hasGlobal(name)) {
  67. const newName = headScope.generateUid(name);
  68. headScope.rename(name, newName);
  69. name = newName;
  70. }
  71. if (capturedInClosure) {
  72. markNeedsBodyWrap();
  73. captured.push(name);
  74. }
  75. if (isForStatement && hasConstantViolations) {
  76. updatedBindingsUsages.set(name, usages);
  77. }
  78. }
  79. }
  80. if (needsBodyWrap) {
  81. const varPath = (0, _loop.wrapLoopBody)(path, captured, updatedBindingsUsages);
  82. if (headPath != null && headPath.isVariableDeclaration()) {
  83. transformBlockScopedVariable(headPath, state, tdzEnabled);
  84. }
  85. varPath.get("declarations.0.init").unwrapFunctionEnvironment();
  86. }
  87. },
  88. VariableDeclaration(path, state) {
  89. transformBlockScopedVariable(path, state, tdzEnabled);
  90. },
  91. ClassDeclaration(path) {
  92. const {
  93. id
  94. } = path.node;
  95. if (!id) return;
  96. const {
  97. scope
  98. } = path.parentPath;
  99. if (!(0, _annexB_3_.isVarScope)(scope) && scope.parent.hasBinding(id.name, {
  100. noUids: true
  101. })) {
  102. path.scope.rename(id.name);
  103. }
  104. }
  105. }])
  106. };
  107. });
  108. const conflictingFunctionsVisitor = {
  109. Scope(path, {
  110. names
  111. }) {
  112. for (const name of names) {
  113. const binding = path.scope.getOwnBinding(name);
  114. if (binding && binding.kind === "hoisted") {
  115. path.scope.rename(name);
  116. }
  117. }
  118. },
  119. "Expression|Declaration"(path) {
  120. path.skip();
  121. }
  122. };
  123. function transformBlockScopedVariable(path, state, tdzEnabled) {
  124. if (!isBlockScoped(path.node)) return;
  125. const dynamicTDZNames = (0, _validation.validateUsage)(path, state, tdzEnabled);
  126. path.node.kind = "var";
  127. const bindingNames = Object.keys(path.getBindingIdentifiers());
  128. for (const name of bindingNames) {
  129. const binding = path.scope.getOwnBinding(name);
  130. if (!binding) continue;
  131. binding.kind = "var";
  132. }
  133. if (isInLoop(path) && !(0, _loop.isVarInLoopHead)(path) || dynamicTDZNames.length > 0) {
  134. for (const decl of path.node.declarations) {
  135. var _decl$init;
  136. (_decl$init = decl.init) != null ? _decl$init : decl.init = path.scope.buildUndefinedNode();
  137. }
  138. }
  139. const blockScope = path.scope;
  140. const varScope = blockScope.getFunctionParent() || blockScope.getProgramParent();
  141. if (varScope !== blockScope) {
  142. for (const name of bindingNames) {
  143. let newName = name;
  144. if (blockScope.parent.hasBinding(name, {
  145. noUids: true
  146. }) || blockScope.parent.hasGlobal(name)) {
  147. newName = blockScope.generateUid(name);
  148. blockScope.rename(name, newName);
  149. }
  150. blockScope.moveBindingTo(newName, varScope);
  151. }
  152. }
  153. blockScope.path.traverse(conflictingFunctionsVisitor, {
  154. names: bindingNames
  155. });
  156. for (const name of dynamicTDZNames) {
  157. path.scope.push({
  158. id: _core.types.identifier(name),
  159. init: state.addHelper("temporalUndefined")
  160. });
  161. }
  162. }
  163. function isLetOrConst(kind) {
  164. return kind === "let" || kind === "const";
  165. }
  166. function isInLoop(path) {
  167. if (!path.parentPath) return false;
  168. if (path.parentPath.isLoop()) return true;
  169. if (path.parentPath.isFunctionParent()) return false;
  170. return isInLoop(path.parentPath);
  171. }
  172. function isBlockScoped(node) {
  173. if (!_core.types.isVariableDeclaration(node)) return false;
  174. if (node[_core.types.BLOCK_SCOPED_SYMBOL]) {
  175. return true;
  176. }
  177. if (!isLetOrConst(node.kind) && node.kind !== "using") {
  178. return false;
  179. }
  180. return true;
  181. }
  182. //# sourceMappingURL=index.js.map