HoistingScope.cs 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. using System.Collections.Generic;
  2. using Esprima.Ast;
  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<VariableDeclaration> _lexicalDeclarations;
  11. internal readonly List<string> _lexicalNames;
  12. private HoistingScope(
  13. List<FunctionDeclaration> functionDeclarations,
  14. List<Key> varNames,
  15. List<VariableDeclaration> variableDeclarations,
  16. List<VariableDeclaration> 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. Script script,
  27. bool collectVarNames = false,
  28. bool collectLexicalNames = false)
  29. {
  30. var treeWalker = new ScriptWalker(StrictModeScope.IsStrictModeCode, 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(
  40. IFunction node,
  41. bool collectVarNames = false,
  42. bool collectLexicalNames = false)
  43. {
  44. var treeWalker = new ScriptWalker(StrictModeScope.IsStrictModeCode, collectVarNames, collectLexicalNames);
  45. treeWalker.Visit(node.Body, null);
  46. return new HoistingScope(
  47. treeWalker._functions,
  48. treeWalker._varNames,
  49. treeWalker._variableDeclarations,
  50. treeWalker._lexicalDeclarations,
  51. treeWalker._lexicalNames);
  52. }
  53. public static List<Declaration> GetLexicalDeclarations(BlockStatement statement)
  54. {
  55. List<Declaration> lexicalDeclarations = null ;
  56. ref readonly var statementListItems = ref statement.Body;
  57. for (var i = 0; i < statementListItems.Count; i++)
  58. {
  59. var node = statementListItems[i];
  60. if (node.Type != Nodes.VariableDeclaration && node.Type != Nodes.FunctionDeclaration)
  61. {
  62. continue;
  63. }
  64. if (node is VariableDeclaration { Kind: VariableDeclarationKind.Var })
  65. {
  66. continue;
  67. }
  68. lexicalDeclarations ??= new List<Declaration>();
  69. lexicalDeclarations.Add((Declaration) node);
  70. }
  71. return lexicalDeclarations;
  72. }
  73. public static List<Declaration> GetLexicalDeclarations(SwitchCase statement)
  74. {
  75. List<Declaration> lexicalDeclarations = null ;
  76. ref readonly var statementListItems = ref statement.Consequent;
  77. for (var i = 0; i < statementListItems.Count; i++)
  78. {
  79. var node = statementListItems[i];
  80. if (node.Type != Nodes.VariableDeclaration)
  81. {
  82. continue;
  83. }
  84. var rootVariable = (VariableDeclaration) node;
  85. if (rootVariable.Kind == VariableDeclarationKind.Var)
  86. {
  87. continue;
  88. }
  89. lexicalDeclarations ??= new List<Declaration>();
  90. lexicalDeclarations.Add(rootVariable);
  91. }
  92. return lexicalDeclarations;
  93. }
  94. private sealed class ScriptWalker
  95. {
  96. internal List<FunctionDeclaration> _functions;
  97. private readonly bool _strict;
  98. private readonly bool _collectVarNames;
  99. internal List<VariableDeclaration> _variableDeclarations;
  100. internal List<Key> _varNames;
  101. private readonly bool _collectLexicalNames;
  102. internal List<VariableDeclaration> _lexicalDeclarations;
  103. internal List<string> _lexicalNames;
  104. public ScriptWalker(bool strict, bool collectVarNames, bool collectLexicalNames)
  105. {
  106. _strict = strict;
  107. _collectVarNames = collectVarNames;
  108. _collectLexicalNames = collectLexicalNames;
  109. }
  110. public void Visit(Node node, Node parent)
  111. {
  112. foreach (var childNode in node.ChildNodes)
  113. {
  114. if (childNode is null)
  115. {
  116. // array expression can push null nodes in Esprima
  117. continue;
  118. }
  119. if (childNode.Type == Nodes.VariableDeclaration)
  120. {
  121. var variableDeclaration = (VariableDeclaration) childNode;
  122. if (variableDeclaration.Kind == VariableDeclarationKind.Var)
  123. {
  124. _variableDeclarations ??= new List<VariableDeclaration>();
  125. _variableDeclarations.Add(variableDeclaration);
  126. if (_collectVarNames)
  127. {
  128. _varNames ??= new List<Key>();
  129. ref readonly var nodeList = ref variableDeclaration.Declarations;
  130. foreach (var declaration in nodeList)
  131. {
  132. if (declaration.Id is Identifier identifier)
  133. {
  134. _varNames.Add(identifier.Name);
  135. }
  136. }
  137. }
  138. }
  139. if (parent is null && variableDeclaration.Kind != VariableDeclarationKind.Var)
  140. {
  141. _lexicalDeclarations ??= new List<VariableDeclaration>();
  142. _lexicalDeclarations.Add(variableDeclaration);
  143. if (_collectLexicalNames)
  144. {
  145. _lexicalNames ??= new List<string>();
  146. ref readonly var nodeList = ref variableDeclaration.Declarations;
  147. foreach (var declaration in nodeList)
  148. {
  149. if (declaration.Id is Identifier identifier)
  150. {
  151. _lexicalNames.Add(identifier.Name);
  152. }
  153. }
  154. }
  155. }
  156. }
  157. else if (childNode.Type == Nodes.FunctionDeclaration
  158. // in strict mode cannot include function declarations directly under block or case clauses
  159. && (!_strict || parent is null || (node.Type != Nodes.BlockStatement && node.Type != Nodes.SwitchCase)))
  160. {
  161. _functions ??= new List<FunctionDeclaration>();
  162. _functions.Add((FunctionDeclaration) childNode);
  163. }
  164. if (childNode.Type != Nodes.FunctionDeclaration
  165. && childNode.Type != Nodes.ArrowFunctionExpression
  166. && childNode.Type != Nodes.ArrowParameterPlaceHolder
  167. && childNode.Type != Nodes.FunctionExpression
  168. && childNode.ChildNodes.Count > 0)
  169. {
  170. Visit(childNode, node);
  171. }
  172. }
  173. }
  174. }
  175. }
  176. }