HoistingScope.cs 14 KB

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