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.

scope.js 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. "use strict";;
  2. var __importDefault = (this && this.__importDefault) || function (mod) {
  3. return (mod && mod.__esModule) ? mod : { "default": mod };
  4. };
  5. Object.defineProperty(exports, "__esModule", { value: true });
  6. var types_1 = __importDefault(require("./types"));
  7. var hasOwn = Object.prototype.hasOwnProperty;
  8. function scopePlugin(fork) {
  9. var types = fork.use(types_1.default);
  10. var Type = types.Type;
  11. var namedTypes = types.namedTypes;
  12. var Node = namedTypes.Node;
  13. var Expression = namedTypes.Expression;
  14. var isArray = types.builtInTypes.array;
  15. var b = types.builders;
  16. var Scope = function Scope(path, parentScope) {
  17. if (!(this instanceof Scope)) {
  18. throw new Error("Scope constructor cannot be invoked without 'new'");
  19. }
  20. ScopeType.assert(path.value);
  21. var depth;
  22. if (parentScope) {
  23. if (!(parentScope instanceof Scope)) {
  24. throw new Error("");
  25. }
  26. depth = parentScope.depth + 1;
  27. }
  28. else {
  29. parentScope = null;
  30. depth = 0;
  31. }
  32. Object.defineProperties(this, {
  33. path: { value: path },
  34. node: { value: path.value },
  35. isGlobal: { value: !parentScope, enumerable: true },
  36. depth: { value: depth },
  37. parent: { value: parentScope },
  38. bindings: { value: {} },
  39. types: { value: {} },
  40. });
  41. };
  42. var scopeTypes = [
  43. // Program nodes introduce global scopes.
  44. namedTypes.Program,
  45. // Function is the supertype of FunctionExpression,
  46. // FunctionDeclaration, ArrowExpression, etc.
  47. namedTypes.Function,
  48. // In case you didn't know, the caught parameter shadows any variable
  49. // of the same name in an outer scope.
  50. namedTypes.CatchClause
  51. ];
  52. var ScopeType = Type.or.apply(Type, scopeTypes);
  53. Scope.isEstablishedBy = function (node) {
  54. return ScopeType.check(node);
  55. };
  56. var Sp = Scope.prototype;
  57. // Will be overridden after an instance lazily calls scanScope.
  58. Sp.didScan = false;
  59. Sp.declares = function (name) {
  60. this.scan();
  61. return hasOwn.call(this.bindings, name);
  62. };
  63. Sp.declaresType = function (name) {
  64. this.scan();
  65. return hasOwn.call(this.types, name);
  66. };
  67. Sp.declareTemporary = function (prefix) {
  68. if (prefix) {
  69. if (!/^[a-z$_]/i.test(prefix)) {
  70. throw new Error("");
  71. }
  72. }
  73. else {
  74. prefix = "t$";
  75. }
  76. // Include this.depth in the name to make sure the name does not
  77. // collide with any variables in nested/enclosing scopes.
  78. prefix += this.depth.toString(36) + "$";
  79. this.scan();
  80. var index = 0;
  81. while (this.declares(prefix + index)) {
  82. ++index;
  83. }
  84. var name = prefix + index;
  85. return this.bindings[name] = types.builders.identifier(name);
  86. };
  87. Sp.injectTemporary = function (identifier, init) {
  88. identifier || (identifier = this.declareTemporary());
  89. var bodyPath = this.path.get("body");
  90. if (namedTypes.BlockStatement.check(bodyPath.value)) {
  91. bodyPath = bodyPath.get("body");
  92. }
  93. bodyPath.unshift(b.variableDeclaration("var", [b.variableDeclarator(identifier, init || null)]));
  94. return identifier;
  95. };
  96. Sp.scan = function (force) {
  97. if (force || !this.didScan) {
  98. for (var name in this.bindings) {
  99. // Empty out this.bindings, just in cases.
  100. delete this.bindings[name];
  101. }
  102. scanScope(this.path, this.bindings, this.types);
  103. this.didScan = true;
  104. }
  105. };
  106. Sp.getBindings = function () {
  107. this.scan();
  108. return this.bindings;
  109. };
  110. Sp.getTypes = function () {
  111. this.scan();
  112. return this.types;
  113. };
  114. function scanScope(path, bindings, scopeTypes) {
  115. var node = path.value;
  116. ScopeType.assert(node);
  117. if (namedTypes.CatchClause.check(node)) {
  118. // A catch clause establishes a new scope but the only variable
  119. // bound in that scope is the catch parameter. Any other
  120. // declarations create bindings in the outer scope.
  121. addPattern(path.get("param"), bindings);
  122. }
  123. else {
  124. recursiveScanScope(path, bindings, scopeTypes);
  125. }
  126. }
  127. function recursiveScanScope(path, bindings, scopeTypes) {
  128. var node = path.value;
  129. if (path.parent &&
  130. namedTypes.FunctionExpression.check(path.parent.node) &&
  131. path.parent.node.id) {
  132. addPattern(path.parent.get("id"), bindings);
  133. }
  134. if (!node) {
  135. // None of the remaining cases matter if node is falsy.
  136. }
  137. else if (isArray.check(node)) {
  138. path.each(function (childPath) {
  139. recursiveScanChild(childPath, bindings, scopeTypes);
  140. });
  141. }
  142. else if (namedTypes.Function.check(node)) {
  143. path.get("params").each(function (paramPath) {
  144. addPattern(paramPath, bindings);
  145. });
  146. recursiveScanChild(path.get("body"), bindings, scopeTypes);
  147. }
  148. else if ((namedTypes.TypeAlias && namedTypes.TypeAlias.check(node)) ||
  149. (namedTypes.InterfaceDeclaration && namedTypes.InterfaceDeclaration.check(node)) ||
  150. (namedTypes.TSTypeAliasDeclaration && namedTypes.TSTypeAliasDeclaration.check(node)) ||
  151. (namedTypes.TSInterfaceDeclaration && namedTypes.TSInterfaceDeclaration.check(node))) {
  152. addTypePattern(path.get("id"), scopeTypes);
  153. }
  154. else if (namedTypes.VariableDeclarator.check(node)) {
  155. addPattern(path.get("id"), bindings);
  156. recursiveScanChild(path.get("init"), bindings, scopeTypes);
  157. }
  158. else if (node.type === "ImportSpecifier" ||
  159. node.type === "ImportNamespaceSpecifier" ||
  160. node.type === "ImportDefaultSpecifier") {
  161. addPattern(
  162. // Esprima used to use the .name field to refer to the local
  163. // binding identifier for ImportSpecifier nodes, but .id for
  164. // ImportNamespaceSpecifier and ImportDefaultSpecifier nodes.
  165. // ESTree/Acorn/ESpree use .local for all three node types.
  166. path.get(node.local ? "local" :
  167. node.name ? "name" : "id"), bindings);
  168. }
  169. else if (Node.check(node) && !Expression.check(node)) {
  170. types.eachField(node, function (name, child) {
  171. var childPath = path.get(name);
  172. if (!pathHasValue(childPath, child)) {
  173. throw new Error("");
  174. }
  175. recursiveScanChild(childPath, bindings, scopeTypes);
  176. });
  177. }
  178. }
  179. function pathHasValue(path, value) {
  180. if (path.value === value) {
  181. return true;
  182. }
  183. // Empty arrays are probably produced by defaults.emptyArray, in which
  184. // case is makes sense to regard them as equivalent, if not ===.
  185. if (Array.isArray(path.value) &&
  186. path.value.length === 0 &&
  187. Array.isArray(value) &&
  188. value.length === 0) {
  189. return true;
  190. }
  191. return false;
  192. }
  193. function recursiveScanChild(path, bindings, scopeTypes) {
  194. var node = path.value;
  195. if (!node || Expression.check(node)) {
  196. // Ignore falsy values and Expressions.
  197. }
  198. else if (namedTypes.FunctionDeclaration.check(node) &&
  199. node.id !== null) {
  200. addPattern(path.get("id"), bindings);
  201. }
  202. else if (namedTypes.ClassDeclaration &&
  203. namedTypes.ClassDeclaration.check(node)) {
  204. addPattern(path.get("id"), bindings);
  205. }
  206. else if (ScopeType.check(node)) {
  207. if (namedTypes.CatchClause.check(node) &&
  208. // TODO Broaden this to accept any pattern.
  209. namedTypes.Identifier.check(node.param)) {
  210. var catchParamName = node.param.name;
  211. var hadBinding = hasOwn.call(bindings, catchParamName);
  212. // Any declarations that occur inside the catch body that do
  213. // not have the same name as the catch parameter should count
  214. // as bindings in the outer scope.
  215. recursiveScanScope(path.get("body"), bindings, scopeTypes);
  216. // If a new binding matching the catch parameter name was
  217. // created while scanning the catch body, ignore it because it
  218. // actually refers to the catch parameter and not the outer
  219. // scope that we're currently scanning.
  220. if (!hadBinding) {
  221. delete bindings[catchParamName];
  222. }
  223. }
  224. }
  225. else {
  226. recursiveScanScope(path, bindings, scopeTypes);
  227. }
  228. }
  229. function addPattern(patternPath, bindings) {
  230. var pattern = patternPath.value;
  231. namedTypes.Pattern.assert(pattern);
  232. if (namedTypes.Identifier.check(pattern)) {
  233. if (hasOwn.call(bindings, pattern.name)) {
  234. bindings[pattern.name].push(patternPath);
  235. }
  236. else {
  237. bindings[pattern.name] = [patternPath];
  238. }
  239. }
  240. else if (namedTypes.AssignmentPattern &&
  241. namedTypes.AssignmentPattern.check(pattern)) {
  242. addPattern(patternPath.get('left'), bindings);
  243. }
  244. else if (namedTypes.ObjectPattern &&
  245. namedTypes.ObjectPattern.check(pattern)) {
  246. patternPath.get('properties').each(function (propertyPath) {
  247. var property = propertyPath.value;
  248. if (namedTypes.Pattern.check(property)) {
  249. addPattern(propertyPath, bindings);
  250. }
  251. else if (namedTypes.Property.check(property)) {
  252. addPattern(propertyPath.get('value'), bindings);
  253. }
  254. else if (namedTypes.SpreadProperty &&
  255. namedTypes.SpreadProperty.check(property)) {
  256. addPattern(propertyPath.get('argument'), bindings);
  257. }
  258. });
  259. }
  260. else if (namedTypes.ArrayPattern &&
  261. namedTypes.ArrayPattern.check(pattern)) {
  262. patternPath.get('elements').each(function (elementPath) {
  263. var element = elementPath.value;
  264. if (namedTypes.Pattern.check(element)) {
  265. addPattern(elementPath, bindings);
  266. }
  267. else if (namedTypes.SpreadElement &&
  268. namedTypes.SpreadElement.check(element)) {
  269. addPattern(elementPath.get("argument"), bindings);
  270. }
  271. });
  272. }
  273. else if (namedTypes.PropertyPattern &&
  274. namedTypes.PropertyPattern.check(pattern)) {
  275. addPattern(patternPath.get('pattern'), bindings);
  276. }
  277. else if ((namedTypes.SpreadElementPattern &&
  278. namedTypes.SpreadElementPattern.check(pattern)) ||
  279. (namedTypes.SpreadPropertyPattern &&
  280. namedTypes.SpreadPropertyPattern.check(pattern))) {
  281. addPattern(patternPath.get('argument'), bindings);
  282. }
  283. }
  284. function addTypePattern(patternPath, types) {
  285. var pattern = patternPath.value;
  286. namedTypes.Pattern.assert(pattern);
  287. if (namedTypes.Identifier.check(pattern)) {
  288. if (hasOwn.call(types, pattern.name)) {
  289. types[pattern.name].push(patternPath);
  290. }
  291. else {
  292. types[pattern.name] = [patternPath];
  293. }
  294. }
  295. }
  296. Sp.lookup = function (name) {
  297. for (var scope = this; scope; scope = scope.parent)
  298. if (scope.declares(name))
  299. break;
  300. return scope;
  301. };
  302. Sp.lookupType = function (name) {
  303. for (var scope = this; scope; scope = scope.parent)
  304. if (scope.declaresType(name))
  305. break;
  306. return scope;
  307. };
  308. Sp.getGlobalScope = function () {
  309. var scope = this;
  310. while (!scope.isGlobal)
  311. scope = scope.parent;
  312. return scope;
  313. };
  314. return Scope;
  315. }
  316. exports.default = scopePlugin;
  317. module.exports = exports["default"];