DebugScopes.cs 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. using Jint.Runtime.Environments;
  2. using System.Collections;
  3. namespace Jint.Runtime.Debugger
  4. {
  5. public sealed class DebugScopes : IReadOnlyList<DebugScope>
  6. {
  7. private readonly HashSet<string> _foundBindings = new();
  8. private readonly List<DebugScope> _scopes = new();
  9. internal DebugScopes(EnvironmentRecord environment)
  10. {
  11. Populate(environment);
  12. }
  13. /// <summary>
  14. /// Shortcut to Global scope.
  15. /// </summary>
  16. /// <remarks>
  17. /// Note that this only includes the object environment record of the Global scope - i.e. it doesn't
  18. /// include block scope bindings (let/const).
  19. /// </remarks>
  20. public DebugScope? Global { get; private set; }
  21. /// <summary>
  22. /// Shortcut to Local scope.
  23. /// </summary>
  24. /// <remarks>
  25. /// Note that this is only present inside functions, and doesn't include block scope bindings (let/const)
  26. /// </remarks>
  27. public DebugScope? Local { get; private set; }
  28. public DebugScope this[int index] => _scopes[index];
  29. public int Count => _scopes.Count;
  30. private void Populate(EnvironmentRecord? environment)
  31. {
  32. bool inLocalScope = true;
  33. while (environment != null)
  34. {
  35. var record = environment;
  36. switch (record)
  37. {
  38. case GlobalEnvironmentRecord global:
  39. // Similarly to Chromium, we split the Global environment into Global and Script scopes
  40. AddScope(DebugScopeType.Script, global._declarativeRecord);
  41. AddScope(DebugScopeType.Global, new ObjectEnvironmentRecord(environment._engine, global._global, false, false));
  42. break;
  43. case FunctionEnvironmentRecord:
  44. AddScope(inLocalScope ? DebugScopeType.Local : DebugScopeType.Closure, record);
  45. // We're now in closure territory
  46. inLocalScope = false;
  47. break;
  48. case ObjectEnvironmentRecord:
  49. // If an ObjectEnvironmentRecord is not a GlobalEnvironmentRecord, it's With
  50. AddScope(DebugScopeType.With, record);
  51. break;
  52. case ModuleEnvironmentRecord:
  53. AddScope(DebugScopeType.Module, record);
  54. break;
  55. case DeclarativeEnvironmentRecord der:
  56. if (der._catchEnvironment)
  57. {
  58. AddScope(DebugScopeType.Catch, record);
  59. }
  60. else
  61. {
  62. bool isTopLevel = environment._outerEnv is FunctionEnvironmentRecord;
  63. AddScope(DebugScopeType.Block, record, isTopLevel: isTopLevel);
  64. }
  65. break;
  66. }
  67. environment = environment._outerEnv;
  68. }
  69. }
  70. private void AddScope(DebugScopeType type, EnvironmentRecord record, bool isTopLevel = false)
  71. {
  72. var bindings = new List<string>();
  73. PopulateBindings(bindings, record);
  74. if (bindings.Count > 0)
  75. {
  76. var scope = new DebugScope(type, record, bindings, isTopLevel);
  77. _scopes.Add(scope);
  78. switch (type)
  79. {
  80. case DebugScopeType.Global:
  81. Global = scope;
  82. break;
  83. case DebugScopeType.Local:
  84. Local = scope;
  85. break;
  86. }
  87. }
  88. }
  89. private void PopulateBindings(List<string> bindings, EnvironmentRecord record)
  90. {
  91. var bindingNames = record.GetAllBindingNames();
  92. foreach (var name in bindingNames)
  93. {
  94. // Only add non-shadowed bindings
  95. if (!_foundBindings.Contains(name))
  96. {
  97. bindings.Add(name);
  98. _foundBindings.Add(name);
  99. }
  100. }
  101. }
  102. public IEnumerator<DebugScope> GetEnumerator()
  103. {
  104. return _scopes.GetEnumerator();
  105. }
  106. IEnumerator IEnumerable.GetEnumerator()
  107. {
  108. return _scopes.GetEnumerator();
  109. }
  110. }
  111. }