Engine.Ast.cs 5.9 KB

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