123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132 |
- using System;
- using Esprima;
- using Esprima.Ast;
- using Jint.Native;
- namespace Jint.Runtime.Debugger
- {
- public class DebugHandler
- {
- public delegate StepMode DebugStepDelegate(object sender, DebugInformation e);
- public delegate StepMode BreakDelegate(object sender, DebugInformation e);
- private enum PauseType
- {
- Step,
- Break
- }
- private readonly Engine _engine;
- private bool _paused;
- private int _steppingDepth;
- public event DebugStepDelegate Step;
- public event BreakDelegate Break;
- internal DebugHandler(Engine engine)
- {
- _engine = engine;
- _steppingDepth = int.MaxValue;
- }
- public BreakPointCollection BreakPoints { get; } = new BreakPointCollection();
- internal void OnStep(Statement statement)
- {
- // Don't reenter if we're already paused (e.g. when evaluating a getter in a Break/Step handler)
- if (_paused)
- {
- return;
- }
- Location location = statement.Location;
- BreakPoint breakpoint = BreakPoints.FindMatch(_engine, location);
- if (breakpoint != null)
- {
- Pause(PauseType.Break, statement);
- }
- else if (_engine.CallStack.Count <= _steppingDepth)
- {
- Pause(PauseType.Step, statement);
- }
- }
- internal void OnReturnPoint(Node functionBody, JsValue returnValue)
- {
- // Don't reenter if we're already paused (e.g. when evaluating a getter in a Break/Step handler)
- if (_paused)
- {
- return;
- }
- var bodyLocation = functionBody.Location;
- var functionBodyEnd = bodyLocation.End;
- var location = new Location(functionBodyEnd, functionBodyEnd, bodyLocation.Source);
- BreakPoint breakpoint = BreakPoints.FindMatch(_engine, location);
- if (breakpoint != null)
- {
- Pause(PauseType.Break, statement: null, location, returnValue);
- }
- else if (_engine.CallStack.Count <= _steppingDepth)
- {
- Pause(PauseType.Step, statement: null, location, returnValue);
- }
- }
- private void Pause(PauseType type, Statement statement = null, Location? location = null, JsValue returnValue = null)
- {
- _paused = true;
-
- DebugInformation info = CreateDebugInformation(statement, location ?? statement.Location, returnValue);
-
- StepMode? result = type switch
- {
- // Conventionally, sender should be DebugHandler - but Engine is more useful
- PauseType.Step => Step?.Invoke(_engine, info),
- PauseType.Break => Break?.Invoke(_engine, info),
- _ => throw new ArgumentException("Invalid pause type", nameof(type))
- };
-
- _paused = false;
-
- HandleNewStepMode(result);
- }
- internal void OnBreak(Statement statement)
- {
- // Don't reenter if we're already paused
- if (_paused)
- {
- return;
- }
- Pause(PauseType.Break, statement);
- }
- private void HandleNewStepMode(StepMode? newStepMode)
- {
- if (newStepMode != null)
- {
- _steppingDepth = newStepMode switch
- {
- StepMode.Over => _engine.CallStack.Count,// Resume stepping when we're back at this level of the call stack
- StepMode.Out => _engine.CallStack.Count - 1,// Resume stepping when we've popped the call stack
- StepMode.None => int.MinValue,// Never step
- _ => int.MaxValue,// Always step
- };
- }
- }
- private DebugInformation CreateDebugInformation(Statement statement, Location? currentLocation, JsValue returnValue)
- {
- return new DebugInformation(
- statement,
- new DebugCallStack(_engine, currentLocation ?? statement.Location, _engine.CallStack, returnValue),
- _engine.CurrentMemoryUsage
- );
- }
- }
- }
|