2
0

DebugScopes.cs 4.3 KB

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