HoistingScope.cs 14 KB

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