Engine.Ast.cs 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. using Esprima;
  2. using Esprima.Ast;
  3. using Jint.Runtime.Environments;
  4. using Jint.Runtime.Interpreter;
  5. using Jint.Runtime.Interpreter.Expressions;
  6. namespace Jint;
  7. public partial class Engine
  8. {
  9. /// <summary>
  10. /// Prepares a script for the engine that includes static analysis data to speed up execution during run-time.
  11. /// </summary>
  12. /// <remarks>
  13. /// Returned instance is reusable and thread-safe. You should prepare scripts only once and then reuse them.
  14. /// </remarks>
  15. public static Script PrepareScript(string script, string? source = null, bool strict = false)
  16. {
  17. var astAnalyzer = new AstAnalyzer();
  18. var options = ParserOptions.Default with
  19. {
  20. AllowReturnOutsideFunction = true,
  21. OnNodeCreated = astAnalyzer.NodeVisitor
  22. };
  23. return new JavaScriptParser(options).ParseScript(script, source, strict);
  24. }
  25. /// <summary>
  26. /// Prepares a module for the engine that includes static analysis data to speed up execution during run-time.
  27. /// </summary>
  28. /// <remarks>
  29. /// Returned instance is reusable and thread-safe. You should prepare modules only once and then reuse them.
  30. /// </remarks>
  31. public static Module PrepareModule(string script, string? source = null)
  32. {
  33. var astAnalyzer = new AstAnalyzer();
  34. var options = ParserOptions.Default with { OnNodeCreated = astAnalyzer.NodeVisitor };
  35. return new JavaScriptParser(options).ParseModule(script, source);
  36. }
  37. private sealed class AstAnalyzer
  38. {
  39. private readonly Dictionary<string, EnvironmentRecord.BindingName> _bindingNames = new();
  40. public void NodeVisitor(Node node)
  41. {
  42. switch (node.Type)
  43. {
  44. case Nodes.Identifier:
  45. {
  46. var name = ((Identifier) node).Name;
  47. if (!_bindingNames.TryGetValue(name, out var bindingName))
  48. {
  49. _bindingNames[name] = bindingName = new EnvironmentRecord.BindingName(name);
  50. }
  51. node.AssociatedData = bindingName;
  52. break;
  53. }
  54. case Nodes.Literal:
  55. node.AssociatedData = JintLiteralExpression.ConvertToJsValue((Literal) node);
  56. break;
  57. case Nodes.ArrowFunctionExpression:
  58. case Nodes.FunctionDeclaration:
  59. case Nodes.FunctionExpression:
  60. var function = (IFunction) node;
  61. node.AssociatedData = JintFunctionDefinition.BuildState(function);
  62. break;
  63. case Nodes.Program:
  64. node.AssociatedData = new CachedHoistingScope((Program) node);
  65. break;
  66. }
  67. }
  68. }
  69. }
  70. internal sealed class CachedHoistingScope
  71. {
  72. public CachedHoistingScope(Program program)
  73. {
  74. Scope = HoistingScope.GetProgramLevelDeclarations(program);
  75. VarNames = new List<string>();
  76. GatherVarNames(Scope, VarNames);
  77. LexNames = new List<CachedLexicalName>();
  78. GatherLexNames(Scope, LexNames);
  79. }
  80. internal static void GatherVarNames(HoistingScope scope, List<string> boundNames)
  81. {
  82. var varDeclarations = scope._variablesDeclarations;
  83. if (varDeclarations != null)
  84. {
  85. for (var i = 0; i < varDeclarations.Count; i++)
  86. {
  87. var d = varDeclarations[i];
  88. d.GetBoundNames(boundNames);
  89. }
  90. }
  91. }
  92. internal static void GatherLexNames(HoistingScope scope, List<CachedLexicalName> boundNames)
  93. {
  94. var lexDeclarations = scope._lexicalDeclarations;
  95. if (lexDeclarations != null)
  96. {
  97. var temp = new List<string>();
  98. for (var i = 0; i < lexDeclarations.Count; i++)
  99. {
  100. var d = lexDeclarations[i];
  101. temp.Clear();
  102. d.GetBoundNames(temp);
  103. foreach (var name in temp)
  104. {
  105. boundNames.Add(new CachedLexicalName(name, d.IsConstantDeclaration()));
  106. }
  107. }
  108. }
  109. }
  110. internal readonly record struct CachedLexicalName(string Name, bool Constant);
  111. public HoistingScope Scope { get; }
  112. public List<string> VarNames { get; }
  113. public List<CachedLexicalName> LexNames { get; }
  114. }
  115. internal static class AstPreparationExtensions
  116. {
  117. internal static HoistingScope GetHoistingScope(this Program program)
  118. {
  119. return program.AssociatedData is CachedHoistingScope cached ? cached.Scope : HoistingScope.GetProgramLevelDeclarations(program);
  120. }
  121. internal static List<string> GetVarNames(this Program program, HoistingScope hoistingScope)
  122. {
  123. List<string> boundNames;
  124. if (program.AssociatedData is CachedHoistingScope cached)
  125. {
  126. boundNames = cached.VarNames;
  127. }
  128. else
  129. {
  130. boundNames = new List<string>();
  131. CachedHoistingScope.GatherVarNames(hoistingScope, boundNames);
  132. }
  133. return boundNames;
  134. }
  135. internal static List<CachedHoistingScope.CachedLexicalName> GetLexNames(this Program program, HoistingScope hoistingScope)
  136. {
  137. List<CachedHoistingScope.CachedLexicalName> boundNames;
  138. if (program.AssociatedData is CachedHoistingScope cached)
  139. {
  140. boundNames = cached.LexNames;
  141. }
  142. else
  143. {
  144. boundNames = new List<CachedHoistingScope.CachedLexicalName>();
  145. CachedHoistingScope.GatherLexNames(hoistingScope, boundNames);
  146. }
  147. return boundNames;
  148. }
  149. }