diff --git a/src/OneScript.DebugProtocol/IDebuggerService.cs b/src/OneScript.DebugProtocol/IDebuggerService.cs
index 84f7593f4..d9edb6cb6 100644
--- a/src/OneScript.DebugProtocol/IDebuggerService.cs
+++ b/src/OneScript.DebugProtocol/IDebuggerService.cs
@@ -17,7 +17,13 @@ public interface IDebuggerService
/// Все точки останова уже установлены, все настройки сделаны
///
void Execute(int threadId);
-
+
+ ///
+ /// Добавление фильтров точек останова для исплючений
+ ///
+ ///
+ void SetMachineExceptionBreakpoints((string Id, string Condition)[] filters);
+
///
/// Установка точек остановки
///
diff --git a/src/OneScript.DebugServices/DefaultBreakpointManager.cs b/src/OneScript.DebugServices/DefaultBreakpointManager.cs
index 9124efea6..09441a8f1 100644
--- a/src/OneScript.DebugServices/DefaultBreakpointManager.cs
+++ b/src/OneScript.DebugServices/DefaultBreakpointManager.cs
@@ -5,17 +5,26 @@ This Source Code Form is subject to the terms of the
at http://mozilla.org/MPL/2.0/.
----------------------------------------------------------*/
+using System;
using System.Collections.Generic;
using System.Linq;
+using OneScript.Commons;
using ScriptEngine.Machine;
namespace OneScript.DebugServices
{
public class DefaultBreakpointManager : IBreakpointManager
{
+ private readonly Dictionary _exceptionBreakpointsFilters = new Dictionary();
private readonly List _breakpoints = new List();
private int _idsGenerator;
+ public void SetExceptionBreakpoints((string Id, string Condition)[] filters)
+ {
+ _exceptionBreakpointsFilters?.Clear();
+ filters?.ForEach(c =>_exceptionBreakpointsFilters.Add(c.Id, c.Condition));
+ }
+
public void SetBreakpoints(string module, (int Line, string Condition)[] breakpoints)
{
var cleaned = _breakpoints.Where(x => x.Module != module)
@@ -27,10 +36,8 @@ public void SetBreakpoints(string module, (int Line, string Condition)[] breakpo
_breakpoints.AddRange(cleaned);
}
- public bool Find(string module, int line)
- {
- return _breakpoints.Find(x => x.Module.Equals(module) && x.LineNumber == line) != null;
- }
+ public bool FindBreakpoint(string module, int line)
+ => _breakpoints.Find(x => x.Module.Equals(module) && x.LineNumber == line) != null;
public string GetCondition(string module, int line)
=> _breakpoints.Find(x => x.Module.Equals(module) && x.LineNumber == line).Condition;
@@ -38,6 +45,26 @@ public string GetCondition(string module, int line)
public void Clear()
{
_breakpoints.Clear();
+ _exceptionBreakpointsFilters.Clear();
+ }
+
+ public bool StopOnAnyException(string message)
+ => NeedStopOnException("all", message);
+
+ public bool StopOnUncaughtException(string message)
+ => NeedStopOnException("uncaught", message);
+
+ private bool NeedStopOnException(string filterId, string message)
+ {
+ if (_exceptionBreakpointsFilters?.TryGetValue(filterId, out var condition) == true)
+ {
+ if (string.IsNullOrEmpty(condition))
+ return true;
+ else
+ return message.Contains(condition);
+ }
+
+ return false;
}
}
}
\ No newline at end of file
diff --git a/src/OneScript.DebugServices/DefaultDebugService.cs b/src/OneScript.DebugServices/DefaultDebugService.cs
index d7a8ac6a4..b4bfdd8b3 100644
--- a/src/OneScript.DebugServices/DefaultDebugService.cs
+++ b/src/OneScript.DebugServices/DefaultDebugService.cs
@@ -51,6 +51,15 @@ public void Execute(int threadId)
}
}
+ public void SetMachineExceptionBreakpoints((string Id, string Condition)[] filters)
+ {
+ _breakpointManager.SetExceptionBreakpoints(filters);
+
+ // Уведомить все потоки о новых фильтрах
+ foreach (var machine in _threadManager.GetAllTokens().Select(x => x.Machine))
+ machine.SetDebugMode(_breakpointManager);
+ }
+
public Breakpoint[] SetMachineBreakpoints(Breakpoint[] breaksToSet)
{
var confirmedBreakpoints = new List();
diff --git a/src/ScriptEngine/Machine/IDebugController.cs b/src/ScriptEngine/Machine/IDebugController.cs
index 1a0524221..7ff1d74c6 100644
--- a/src/ScriptEngine/Machine/IDebugController.cs
+++ b/src/ScriptEngine/Machine/IDebugController.cs
@@ -24,9 +24,15 @@ public interface IDebugController : IDisposable
public interface IBreakpointManager
{
+ void SetExceptionBreakpoints((string Id, string Condition)[] filters);
+
void SetBreakpoints(string module, (int Line, string Condition)[] breakpoints);
+
+ bool StopOnAnyException(string message);
+
+ bool StopOnUncaughtException(string message);
- bool Find(string module, int line);
+ bool FindBreakpoint(string module, int line);
string GetCondition(string module, int line);
diff --git a/src/ScriptEngine/Machine/MachineInstance.cs b/src/ScriptEngine/Machine/MachineInstance.cs
index 1f7b21496..f5a9d447c 100644
--- a/src/ScriptEngine/Machine/MachineInstance.cs
+++ b/src/ScriptEngine/Machine/MachineInstance.cs
@@ -409,7 +409,14 @@ private void ExecuteCode()
{
SetScriptExceptionSource(exc);
- if (ShouldRethrowException(exc))
+ var shouldRethrow = ShouldRethrowException(exc);
+
+ if (MachineStopped != null && _stopManager != null)
+ if (_stopManager.Breakpoints.StopOnAnyException(exc.MessageWithoutCodeFragment) ||
+ shouldRethrow && _stopManager.Breakpoints.StopOnUncaughtException(exc.MessageWithoutCodeFragment))
+ EmitStopOnException();
+
+ if (shouldRethrow)
throw;
}
}
@@ -1297,6 +1304,16 @@ private void LineNum(int arg)
NextInstruction();
}
+ private void EmitStopOnException()
+ {
+ if (MachineStopped != null && _stopManager != null)
+ {
+ CreateFullCallstack();
+ var args = new MachineStoppedEventArgs(MachineStopReason.Exception, Environment.CurrentManagedThreadId, "");
+ MachineStopped?.Invoke(this, args);
+ }
+ }
+
private void EmitStopEventIfNecessary()
{
if (MachineStopped != null && _stopManager != null && _stopManager.ShouldStopAtThisLine(_module.Source.Location, _currentFrame))
diff --git a/src/ScriptEngine/Machine/MachineStopManager.cs b/src/ScriptEngine/Machine/MachineStopManager.cs
index c8b1c3359..fcb5b2aa6 100644
--- a/src/ScriptEngine/Machine/MachineStopManager.cs
+++ b/src/ScriptEngine/Machine/MachineStopManager.cs
@@ -19,8 +19,6 @@ internal enum DebugState
SteppingOut
}
-
-
internal class MachineStopManager
{
private struct StopPoint
@@ -119,7 +117,7 @@ public bool ShouldStopAtThisLine(string module, ExecutionFrame currentFrame)
private bool HitBreakpointOnLine(string module, ExecutionFrame currentFrame)
{
- return _breakpoints.Find(module, currentFrame.LineNumber);
+ return _breakpoints.FindBreakpoint(module, currentFrame.LineNumber);
}
private bool FrameIsInStopList(ExecutionFrame currentFrame)
diff --git a/src/VSCode.DebugAdapter/DebugSession.cs b/src/VSCode.DebugAdapter/DebugSession.cs
index ced5ff0e3..85fd8faf9 100644
--- a/src/VSCode.DebugAdapter/DebugSession.cs
+++ b/src/VSCode.DebugAdapter/DebugSession.cs
@@ -118,7 +118,13 @@ public class Breakpoint
public bool verified { get; }
public int line { get; }
- public Breakpoint(bool verified, int line) {
+ public Breakpoint(bool verified)
+ {
+ this.verified = verified;
+ line = 0;
+ }
+
+ public Breakpoint(bool verified, int line) {
this.verified = verified;
this.line = line;
}
@@ -180,6 +186,7 @@ public class Capabilities : ResponseBody {
public bool supportsFunctionBreakpoints;
public bool supportsConditionalBreakpoints;
public bool supportsEvaluateForHovers;
+ public bool supportsExceptionFilterOptions;
public dynamic[] exceptionBreakpointFilters;
public bool supportTerminateDebuggee;
}
@@ -262,15 +269,28 @@ public class SetBreakpointsResponseBody : ResponseBody
public SetBreakpointsResponseBody(List bpts = null) {
if (bpts == null)
- breakpoints = new Breakpoint[0];
- else
+ breakpoints = Array.Empty();
+ else
breakpoints = bpts.ToArray();
}
}
- // ---- The Session --------------------------------------------------------
+ public class SetExceptionBreakpointsResponseBody : ResponseBody
+ {
+ public Breakpoint[] breakpoints { get; }
+
+ public SetExceptionBreakpointsResponseBody(List bpts = null)
+ {
+ if (bpts == null)
+ breakpoints = Array.Empty();
+ else
+ breakpoints = bpts.ToArray();
+ }
+ }
+
+ // ---- The Session --------------------------------------------------------
- public abstract class DebugSession : ProtocolServer
+ public abstract class DebugSession : ProtocolServer
{
private bool _clientLinesStartAt1 = true;
private bool _clientPathsAreUri = true;
diff --git a/src/VSCode.DebugAdapter/DebugeeProcess.cs b/src/VSCode.DebugAdapter/DebugeeProcess.cs
index a34ec438f..61769232f 100644
--- a/src/VSCode.DebugAdapter/DebugeeProcess.cs
+++ b/src/VSCode.DebugAdapter/DebugeeProcess.cs
@@ -201,6 +201,11 @@ public void Kill()
_process.WaitForExit(1500);
}
+ public void SetExceptionsBreakpoints((string Id, string Condition)[] filters)
+ {
+ _debugger.SetMachineExceptionBreakpoints(filters);
+ }
+
public Breakpoint[] SetBreakpoints(IEnumerable breakpoints)
{
var confirmedBreaks = _debugger.SetMachineBreakpoints(breakpoints.ToArray());
diff --git a/src/VSCode.DebugAdapter/OscriptDebugSession.cs b/src/VSCode.DebugAdapter/OscriptDebugSession.cs
index 327db85e4..cea13a8b2 100644
--- a/src/VSCode.DebugAdapter/OscriptDebugSession.cs
+++ b/src/VSCode.DebugAdapter/OscriptDebugSession.cs
@@ -9,9 +9,11 @@ This Source Code Form is subject to the terms of the
using System.IO;
using System.Runtime.CompilerServices;
using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
using OneScript.DebugProtocol;
using Serilog;
using VSCodeDebug;
+using static System.Net.WebRequestMethods;
namespace VSCode.DebugAdapter
@@ -43,7 +45,26 @@ public override void Initialize(Response response, dynamic args)
supportsConditionalBreakpoints = true,
supportsFunctionBreakpoints = false,
supportsConfigurationDoneRequest = true,
- exceptionBreakpointFilters = new dynamic[0],
+ supportsExceptionFilterOptions = true,
+ exceptionBreakpointFilters = new dynamic[]
+ {
+ new
+ {
+ filter = "uncaught",
+ label = "Необработанные исключения",
+ description = "Остановка при возникновении необработанного исключения",
+ supportsCondition = true,
+ conditionDescription = "Искомая подстрока текста исключения"
+ },
+ new
+ {
+ filter = "all",
+ label = "Все исключения",
+ description = "Остановка при возникновении любого исключения",
+ supportsCondition = true,
+ conditionDescription = "Искомая подстрока текста исключения"
+ }
+ },
supportsEvaluateForHovers = true,
supportTerminateDebuggee = true
});
@@ -113,7 +134,7 @@ public override void Launch(Response response, dynamic args)
public override void Attach(Response response, dynamic arguments)
{
LogCommandReceived();
- _process.DebugPort = getInt(arguments, "debugPort", 2801);
+ _process.DebugPort = GetFromContainer(arguments, "debugPort", 2801);
_process.ProcessExited += (s, e) =>
{
Log.Information("Debuggee has exited");
@@ -148,6 +169,31 @@ public override void Disconnect(Response response, dynamic arguments)
SendResponse(response);
}
+ public override void SetExceptionBreakpoints(Response response, dynamic arguments)
+ {
+ LogCommandReceived(arguments);
+ Log.Debug("Exception breakpoints: {Data}", JsonConvert.SerializeObject(arguments));
+
+ var acceptedFilters = new List();
+ var filters = new List<(string Id, string Condition)>();
+
+ foreach(var filter in arguments.filters)
+ {
+ filters.Add((filter, ""));
+ acceptedFilters.Add(new VSCodeDebug.Breakpoint(true));
+ }
+
+ foreach (var filterOption in arguments.filterOptions)
+ {
+ filters.Add((filterOption.filterId, filterOption.condition ?? ""));
+ acceptedFilters.Add(new VSCodeDebug.Breakpoint(true));
+ }
+
+ _process.SetExceptionsBreakpoints(filters.ToArray());
+
+ SendResponse(response, new SetExceptionBreakpointsResponseBody(acceptedFilters));
+ }
+
public override void SetBreakpoints(Response response, dynamic arguments)
{
LogCommandReceived();
@@ -322,7 +368,7 @@ public override void StackTrace(Response response, dynamic arguments)
public override void Scopes(Response response, dynamic arguments)
{
LogCommandReceived();
- int frameId = getInt(arguments, "frameId");
+ int frameId = GetFromContainer(arguments, "frameId", 0);
var frame = _framesHandles.Get(frameId, null);
if (frame == null)
{
@@ -338,7 +384,7 @@ public override void Scopes(Response response, dynamic arguments)
public override void Variables(Response response, dynamic arguments)
{
LogCommandReceived();
- int varsHandle = getInt(arguments, "variablesReference");
+ int varsHandle = GetFromContainer(arguments, "variablesReference", 0);
var variables = _variableHandles.Get(varsHandle, null);
if (variables == null)
{
@@ -386,7 +432,7 @@ public override void Evaluate(Response response, dynamic arguments)
{
LogCommandReceived();
// expression, frameId, context
- int frameId = getInt(arguments, "frameId");
+ int frameId = GetFromContainer(arguments, "frameId", 0);
var frame = _framesHandles.Get(frameId, null);
if (frame == null)
{
@@ -438,11 +484,11 @@ private void SendOutput(string category, string data)
}
}
- private static int getInt(dynamic container, string propertyName, int dflt = 0)
+ private static T GetFromContainer(dynamic container, string propertyName, T dflt = default)
{
try
{
- return (int)container[propertyName];
+ return (T)container[propertyName];
}
catch (Exception)
{
diff --git a/src/VSCode.DebugAdapter/OscriptProtocols/Tcp/TcpDebugServerClient.cs b/src/VSCode.DebugAdapter/OscriptProtocols/Tcp/TcpDebugServerClient.cs
index 27a7165d7..6aa5f131e 100644
--- a/src/VSCode.DebugAdapter/OscriptProtocols/Tcp/TcpDebugServerClient.cs
+++ b/src/VSCode.DebugAdapter/OscriptProtocols/Tcp/TcpDebugServerClient.cs
@@ -133,6 +133,11 @@ public void Execute(int threadId)
WriteCommand(threadId);
}
+ public void SetMachineExceptionBreakpoints((string Id, string Condition)[] filters)
+ {
+ WriteCommand(filters);
+ }
+
public Breakpoint[] SetMachineBreakpoints(Breakpoint[] breaksToSet)
{
WriteCommand(breaksToSet);