HoistingScope.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. using Esprima.Ast;
  2. using Jint.Runtime.Modules;
  3. namespace Jint
  4. {
  5. internal sealed class HoistingScope
  6. {
  7. internal readonly List<FunctionDeclaration>? _functionDeclarations;
  8. internal readonly List<VariableDeclaration>? _variablesDeclarations;
  9. internal readonly List<Key>? _varNames;
  10. internal readonly List<Declaration>? _lexicalDeclarations;
  11. internal readonly List<string>? _lexicalNames;
  12. private HoistingScope(
  13. List<FunctionDeclaration>? functionDeclarations,
  14. List<Key>? varNames,
  15. List<VariableDeclaration>? variableDeclarations,
  16. List<Declaration>? lexicalDeclarations,
  17. List<string>? lexicalNames)
  18. {
  19. _functionDeclarations = functionDeclarations;
  20. _varNames = varNames;
  21. _variablesDeclarations = variableDeclarations;
  22. _lexicalDeclarations = lexicalDeclarations;
  23. _lexicalNames = lexicalNames;
  24. }
  25. public static HoistingScope GetProgramLevelDeclarations(
  26. Program script,
  27. bool collectVarNames = false,
  28. bool collectLexicalNames = false)
  29. {
  30. var treeWalker = new ScriptWalker(collectVarNames, collectLexicalNames);
  31. treeWalker.Visit(script, null);
  32. return new HoistingScope(
  33. treeWalker._functions,
  34. treeWalker._varNames,
  35. treeWalker._variableDeclarations,
  36. treeWalker._lexicalDeclarations,
  37. treeWalker._lexicalNames);
  38. }
  39. public static HoistingScope GetFunctionLevelDeclarations(bool strict, IFunction node)
  40. {
  41. var treeWalker = new ScriptWalker(collectVarNames: true, collectLexicalNames: true);
  42. treeWalker.Visit(node.Body, null);
  43. return new HoistingScope(
  44. treeWalker._functions,
  45. treeWalker._varNames,
  46. treeWalker._variableDeclarations,
  47. treeWalker._lexicalDeclarations,
  48. treeWalker._lexicalNames);
  49. }
  50. public static HoistingScope GetModuleLevelDeclarations(
  51. Module module,
  52. bool collectVarNames = false,
  53. bool collectLexicalNames = false)
  54. {
  55. // modules area always strict
  56. var treeWalker = new ScriptWalker(collectVarNames, collectLexicalNames);
  57. treeWalker.Visit(module, null);
  58. return new HoistingScope(
  59. treeWalker._functions,
  60. treeWalker._varNames,
  61. treeWalker._variableDeclarations,
  62. treeWalker._lexicalDeclarations,
  63. treeWalker._lexicalNames);
  64. }
  65. public static List<Declaration>? GetLexicalDeclarations(BlockStatement statement)
  66. {
  67. List<Declaration>? lexicalDeclarations = null;
  68. ref readonly var statementListItems = ref statement.Body;
  69. for (var i = 0; i < statementListItems.Count; i++)
  70. {
  71. var node = statementListItems[i];
  72. if (node.Type != Nodes.VariableDeclaration && node.Type != Nodes.FunctionDeclaration && node.Type != Nodes.ClassDeclaration)
  73. {
  74. continue;
  75. }
  76. if (node is VariableDeclaration { Kind: VariableDeclarationKind.Var })
  77. {
  78. continue;
  79. }
  80. lexicalDeclarations ??= new List<Declaration>();
  81. lexicalDeclarations.Add((Declaration)node);
  82. }
  83. return lexicalDeclarations;
  84. }
  85. public static List<Declaration>? GetLexicalDeclarations(SwitchCase statement)
  86. {
  87. List<Declaration>? lexicalDeclarations = null;
  88. ref readonly var statementListItems = ref statement.Consequent;
  89. for (var i = 0; i < statementListItems.Count; i++)
  90. {
  91. var node = statementListItems[i];
  92. if (node.Type != Nodes.VariableDeclaration)
  93. {
  94. continue;
  95. }
  96. var rootVariable = (VariableDeclaration)node;
  97. if (rootVariable.Kind == VariableDeclarationKind.Var)
  98. {
  99. continue;
  100. }
  101. lexicalDeclarations ??= new List<Declaration>();
  102. lexicalDeclarations.Add(rootVariable);
  103. }
  104. return lexicalDeclarations;
  105. }
  106. public static void GetImportsAndExports(
  107. Module module,
  108. out HashSet<string> requestedModules,
  109. out List<ImportEntry>? importEntries,
  110. out List<ExportEntry> localExportEntries,
  111. out List<ExportEntry> indirectExportEntries,
  112. out List<ExportEntry> starExportEntries)
  113. {
  114. var treeWalker = new ModuleWalker();
  115. treeWalker.Visit(module);
  116. importEntries = treeWalker._importEntries;
  117. requestedModules = treeWalker._requestedModules ?? new(StringComparer.Ordinal);
  118. var importedBoundNames = new HashSet<string>(StringComparer.Ordinal);
  119. if (importEntries != null)
  120. {
  121. for (var i = 0; i < importEntries.Count; i++)
  122. {
  123. var ie = importEntries[i];
  124. if (ie.LocalName is not null)
  125. {
  126. importedBoundNames.Add(ie.LocalName);
  127. }
  128. }
  129. }
  130. var exportEntries = treeWalker._exportEntries;
  131. localExportEntries = new();
  132. indirectExportEntries = new();
  133. starExportEntries = new();
  134. if (exportEntries != null)
  135. {
  136. for (var i = 0; i < exportEntries.Count; i++)
  137. {
  138. var ee = exportEntries[i];
  139. if (ee.ModuleRequest is null)
  140. {
  141. if (!importedBoundNames.Contains(ee.LocalName))
  142. {
  143. localExportEntries.Add(ee);
  144. }
  145. else
  146. {
  147. for (var j = 0; j < importEntries!.Count; j++)
  148. {
  149. var ie = importEntries[j];
  150. if (string.Equals(ie.LocalName, ee.LocalName, StringComparison.Ordinal))
  151. {
  152. if (string.Equals(ie.ImportName, "*", StringComparison.Ordinal))
  153. {
  154. localExportEntries.Add(ee);
  155. }
  156. else
  157. {
  158. indirectExportEntries.Add(new(ee.ExportName, ie.ModuleRequest, ie.ImportName, null));
  159. }
  160. break;
  161. }
  162. }
  163. }
  164. }
  165. else if (string.Equals(ee.ImportName, "*", StringComparison.Ordinal) && ee.ExportName is null)
  166. {
  167. starExportEntries.Add(ee);
  168. }
  169. else
  170. {
  171. indirectExportEntries.Add(ee);
  172. }
  173. }
  174. }
  175. }
  176. private sealed class ScriptWalker
  177. {
  178. internal List<FunctionDeclaration>? _functions;
  179. private readonly bool _collectVarNames;
  180. internal List<VariableDeclaration>? _variableDeclarations;
  181. internal List<Key>? _varNames;
  182. private readonly bool _collectLexicalNames;
  183. internal List<Declaration>? _lexicalDeclarations;
  184. internal List<string>? _lexicalNames;
  185. public ScriptWalker(bool collectVarNames, bool collectLexicalNames)
  186. {
  187. _collectVarNames = collectVarNames;
  188. _collectLexicalNames = collectLexicalNames;
  189. }
  190. public void Visit(Node node, Node? parent)
  191. {
  192. foreach (var childNode in node.ChildNodes)
  193. {
  194. var childType = childNode.Type;
  195. if (childType == Nodes.VariableDeclaration)
  196. {
  197. var variableDeclaration = (VariableDeclaration)childNode;
  198. if (variableDeclaration.Kind == VariableDeclarationKind.Var)
  199. {
  200. _variableDeclarations ??= new List<VariableDeclaration>();
  201. _variableDeclarations.Add(variableDeclaration);
  202. if (_collectVarNames)
  203. {
  204. _varNames ??= new List<Key>();
  205. ref readonly var nodeList = ref variableDeclaration.Declarations;
  206. foreach (var declaration in nodeList)
  207. {
  208. if (declaration.Id is Identifier identifier)
  209. {
  210. _varNames.Add(identifier.Name);
  211. }
  212. }
  213. }
  214. }
  215. if (parent is null or Module && variableDeclaration.Kind != VariableDeclarationKind.Var)
  216. {
  217. _lexicalDeclarations ??= new List<Declaration>();
  218. _lexicalDeclarations.Add(variableDeclaration);
  219. if (_collectLexicalNames)
  220. {
  221. _lexicalNames ??= new List<string>();
  222. ref readonly var nodeList = ref variableDeclaration.Declarations;
  223. foreach (var declaration in nodeList)
  224. {
  225. if (declaration.Id is Identifier identifier)
  226. {
  227. _lexicalNames.Add(identifier.Name);
  228. }
  229. }
  230. }
  231. }
  232. }
  233. else if (childType == Nodes.FunctionDeclaration)
  234. {
  235. // function declarations are not hoisted if they are under block or case clauses
  236. if (parent is null || (node.Type != Nodes.BlockStatement && node.Type != Nodes.SwitchCase))
  237. {
  238. _functions ??= new List<FunctionDeclaration>();
  239. _functions.Add((FunctionDeclaration)childNode);
  240. }
  241. }
  242. else if (childType == Nodes.ClassDeclaration && parent is null or Module)
  243. {
  244. _lexicalDeclarations ??= new List<Declaration>();
  245. _lexicalDeclarations.Add((Declaration) childNode);
  246. }
  247. if (childType != Nodes.FunctionDeclaration
  248. && childType != Nodes.ArrowFunctionExpression
  249. && childType != Nodes.FunctionExpression
  250. && !childNode.ChildNodes.IsEmpty())
  251. {
  252. Visit(childNode, node);
  253. }
  254. }
  255. }
  256. }
  257. private sealed class ModuleWalker
  258. {
  259. internal List<ImportEntry>? _importEntries;
  260. internal List<ExportEntry>? _exportEntries;
  261. internal HashSet<string>? _requestedModules;
  262. internal void Visit(Node node)
  263. {
  264. foreach (var childNode in node.ChildNodes)
  265. {
  266. if (childNode.Type == Nodes.ImportDeclaration)
  267. {
  268. _importEntries ??= new();
  269. _requestedModules ??= new(StringComparer.Ordinal);
  270. var import = (ImportDeclaration) childNode;
  271. import.GetImportEntries(_importEntries, _requestedModules);
  272. }
  273. else if (childNode.Type is Nodes.ExportAllDeclaration or Nodes.ExportDefaultDeclaration or Nodes.ExportNamedDeclaration)
  274. {
  275. _exportEntries ??= new();
  276. _requestedModules ??= new(StringComparer.Ordinal);
  277. var export = (ExportDeclaration) childNode;
  278. export.GetExportEntries(_exportEntries, _requestedModules);
  279. }
  280. if (!childNode.ChildNodes.IsEmpty())
  281. {
  282. Visit(childNode);
  283. }
  284. }
  285. }
  286. }
  287. }
  288. }