DebugHandler.cs 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. using System;
  2. using Esprima;
  3. using Esprima.Ast;
  4. using Jint.Native;
  5. namespace Jint.Runtime.Debugger
  6. {
  7. public class DebugHandler
  8. {
  9. public delegate StepMode DebugStepDelegate(object sender, DebugInformation e);
  10. public delegate StepMode BreakDelegate(object sender, DebugInformation e);
  11. private enum PauseType
  12. {
  13. Step,
  14. Break
  15. }
  16. private readonly Engine _engine;
  17. private bool _paused;
  18. private int _steppingDepth;
  19. public event DebugStepDelegate Step;
  20. public event BreakDelegate Break;
  21. internal DebugHandler(Engine engine)
  22. {
  23. _engine = engine;
  24. _steppingDepth = int.MaxValue;
  25. }
  26. public BreakPointCollection BreakPoints { get; } = new BreakPointCollection();
  27. internal void OnStep(Statement statement)
  28. {
  29. // Don't reenter if we're already paused (e.g. when evaluating a getter in a Break/Step handler)
  30. if (_paused)
  31. {
  32. return;
  33. }
  34. Location location = statement.Location;
  35. BreakPoint breakpoint = BreakPoints.FindMatch(_engine, location);
  36. if (breakpoint != null)
  37. {
  38. Pause(PauseType.Break, statement);
  39. }
  40. else if (_engine.CallStack.Count <= _steppingDepth)
  41. {
  42. Pause(PauseType.Step, statement);
  43. }
  44. }
  45. internal void OnReturnPoint(Node functionBody, JsValue returnValue)
  46. {
  47. // Don't reenter if we're already paused (e.g. when evaluating a getter in a Break/Step handler)
  48. if (_paused)
  49. {
  50. return;
  51. }
  52. var bodyLocation = functionBody.Location;
  53. var functionBodyEnd = bodyLocation.End;
  54. var location = new Location(functionBodyEnd, functionBodyEnd, bodyLocation.Source);
  55. BreakPoint breakpoint = BreakPoints.FindMatch(_engine, location);
  56. if (breakpoint != null)
  57. {
  58. Pause(PauseType.Break, statement: null, location, returnValue);
  59. }
  60. else if (_engine.CallStack.Count <= _steppingDepth)
  61. {
  62. Pause(PauseType.Step, statement: null, location, returnValue);
  63. }
  64. }
  65. private void Pause(PauseType type, Statement statement = null, Location? location = null, JsValue returnValue = null)
  66. {
  67. _paused = true;
  68. DebugInformation info = CreateDebugInformation(statement, location ?? statement.Location, returnValue);
  69. StepMode? result = type switch
  70. {
  71. // Conventionally, sender should be DebugHandler - but Engine is more useful
  72. PauseType.Step => Step?.Invoke(_engine, info),
  73. PauseType.Break => Break?.Invoke(_engine, info),
  74. _ => throw new ArgumentException("Invalid pause type", nameof(type))
  75. };
  76. _paused = false;
  77. HandleNewStepMode(result);
  78. }
  79. internal void OnBreak(Statement statement)
  80. {
  81. // Don't reenter if we're already paused
  82. if (_paused)
  83. {
  84. return;
  85. }
  86. Pause(PauseType.Break, statement);
  87. }
  88. private void HandleNewStepMode(StepMode? newStepMode)
  89. {
  90. if (newStepMode != null)
  91. {
  92. _steppingDepth = newStepMode switch
  93. {
  94. StepMode.Over => _engine.CallStack.Count,// Resume stepping when we're back at this level of the call stack
  95. StepMode.Out => _engine.CallStack.Count - 1,// Resume stepping when we've popped the call stack
  96. StepMode.None => int.MinValue,// Never step
  97. _ => int.MaxValue,// Always step
  98. };
  99. }
  100. }
  101. private DebugInformation CreateDebugInformation(Statement statement, Location? currentLocation, JsValue returnValue)
  102. {
  103. return new DebugInformation(
  104. statement,
  105. new DebugCallStack(_engine, currentLocation ?? statement.Location, _engine.CallStack, returnValue),
  106. _engine.CurrentMemoryUsage
  107. );
  108. }
  109. }
  110. }