DebugScopes.cs 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  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 DeclarativeEnvironmentRecord der:
  53. if (der._catchEnvironment)
  54. {
  55. AddScope(DebugScopeType.Catch, record);
  56. }
  57. else
  58. {
  59. bool isTopLevel = environment._outerEnv is FunctionEnvironmentRecord;
  60. AddScope(DebugScopeType.Block, record, isTopLevel: isTopLevel);
  61. }
  62. break;
  63. }
  64. environment = environment._outerEnv;
  65. }
  66. }
  67. private void AddScope(DebugScopeType type, EnvironmentRecord record, bool isTopLevel = false)
  68. {
  69. var bindings = new List<string>();
  70. PopulateBindings(bindings, record);
  71. if (bindings.Count > 0)
  72. {
  73. var scope = new DebugScope(type, record, bindings, isTopLevel);
  74. _scopes.Add(scope);
  75. switch (type)
  76. {
  77. case DebugScopeType.Global:
  78. Global = scope;
  79. break;
  80. case DebugScopeType.Local:
  81. Local = scope;
  82. break;
  83. }
  84. }
  85. }
  86. private void PopulateBindings(List<string> bindings, EnvironmentRecord record)
  87. {
  88. var bindingNames = record.GetAllBindingNames();
  89. foreach (var name in bindingNames)
  90. {
  91. // Only add non-shadowed bindings
  92. if (!_foundBindings.Contains(name))
  93. {
  94. bindings.Add(name);
  95. _foundBindings.Add(name);
  96. }
  97. }
  98. }
  99. public IEnumerator<DebugScope> GetEnumerator()
  100. {
  101. return _scopes.GetEnumerator();
  102. }
  103. IEnumerator IEnumerable.GetEnumerator()
  104. {
  105. return _scopes.GetEnumerator();
  106. }
  107. }
  108. }