Engine.Ast.cs 5.9 KB

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