private static HttpClient client = new HttpClient();
private HashSet<SessionId> sessions = new HashSet<SessionId>();
private Dictionary<SessionId, ExecutionContext> contexts = new Dictionary<SessionId, ExecutionContext>();
+ private const string sPauseOnUncaught = "pause_on_uncaught";
+ private const string sPauseOnCaught = "pause_on_caught";
public MonoProxy(ILoggerFactory loggerFactory, IList<string> urlSymbolServerList) : base(loggerFactory)
{
return true;
}
+ case "Runtime.exceptionThrown":
+ {
+ if (!GetContext(sessionId).IsRuntimeReady)
+ {
+ string exceptionError = args?["exceptionDetails"]?["exception"]?["value"]?.Value<string>();
+ if (exceptionError == sPauseOnUncaught || exceptionError == sPauseOnCaught)
+ {
+ return true;
+ }
+ }
+ break;
+ }
+
case "Debugger.paused":
{
+ if (!GetContext(sessionId).IsRuntimeReady)
+ {
+ string reason = args?["reason"]?.Value<string>();
+ if (reason == "exception")
+ {
+ string exceptionError = args?["data"]?["value"]?.Value<string>();
+ if (exceptionError == sPauseOnUncaught)
+ {
+ await SendCommand(sessionId, "Debugger.resume", new JObject(), token);
+ GetContext(sessionId).PauseOnUncaught = true;
+ return true;
+ }
+ if (exceptionError == sPauseOnCaught)
+ {
+ await SendCommand(sessionId, "Debugger.resume", new JObject(), token);
+ GetContext(sessionId).PauseOnCaught = true;
+ return true;
+ }
+ }
+ }
//TODO figure out how to stich out more frames and, in particular what happens when real wasm is on the stack
string top_func = args?["callFrames"]?[0]?["functionName"]?.Value<string>();
switch (top_func) {
case "Debugger.setPauseOnExceptions":
{
string state = args["state"].Value<string>();
- await sdbHelper.EnableExceptions(id, state, token);
+ if (!context.IsRuntimeReady)
+ {
+ context.PauseOnCaught = false;
+ context.PauseOnUncaught = false;
+ switch (state)
+ {
+ case "all":
+ context.PauseOnCaught = true;
+ context.PauseOnUncaught = true;
+ break;
+ case "uncaught":
+ context.PauseOnUncaught = true;
+ break;
+ }
+ }
+ else
+ await sdbHelper.EnableExceptions(id, state, token);
// Pass this on to JS too
return false;
}
Log("verbose", $"Failed to clear breakpoints");
}
+ if (context.PauseOnCaught && context.PauseOnUncaught)
+ await sdbHelper.EnableExceptions(sessionId, "all", token);
+ else if (context.PauseOnUncaught)
+ await sdbHelper.EnableExceptions(sessionId, "uncaught", token);
+
await sdbHelper.SetProtocolVersion(sessionId, token);
await sdbHelper.EnableReceiveUserBreakRequest(sessionId, token);
// see https://github.com/mono/mono/issues/19549 for background
if (sessions.Add(sessionId))
{
+ string checkUncaughtExceptions = $"throw \"{sPauseOnUncaught}\";";
+ string checkCaughtExceptions = $"try {{throw \"{sPauseOnCaught}\";}} catch {{}}";
await SendMonoCommand(sessionId, new MonoCommands("globalThis.dotnetDebugger = true"), token);
Result res = await SendCommand(sessionId,
"Page.addScriptToEvaluateOnNewDocument",
- JObject.FromObject(new { source = "globalThis.dotnetDebugger = true; delete navigator.constructor.prototype.webdriver" }),
+ JObject.FromObject(new { source = $"globalThis.dotnetDebugger = true; delete navigator.constructor.prototype.webdriver; {checkCaughtExceptions} {checkUncaughtExceptions}" }),
token);
if (sessionId != SessionId.Null && !res.IsOk)
using System.Threading.Tasks;
using Microsoft.WebAssembly.Diagnostics;
using Newtonsoft.Json.Linq;
+using System.Threading;
using Xunit;
namespace DebuggerTests
CheckString(exception_members, "message", exception_message);
}
+ [Fact]
+ public async Task ExceptionTestUncaughtWithReload()
+ {
+ string entry_method_name = "[debugger-test] DebuggerTests.ExceptionTestsClass:TestExceptions";
+ var debugger_test_loc = "dotnet://debugger-test.dll/debugger-exception-test.cs";
+
+ await SetPauseOnException("uncaught");
+
+ await SendCommand("Page.enable", null);
+ await SendCommand("Page.reload", JObject.FromObject(new
+ {
+ ignoreCache = true
+ }));
+ Thread.Sleep(1000);
+
+ var eval_expr = "window.setTimeout(function() { invoke_static_method (" +
+ $"'{entry_method_name}'" +
+ "); }, 1);";
+
+ var pause_location = await EvaluateAndCheck(eval_expr, null, 0, 0, null);
+ //stop in the managed caught exception
+ pause_location = await WaitForManagedException(pause_location);
+
+ AssertEqual("run", pause_location["callFrames"]?[0]?["functionName"]?.Value<string>(), "pause1");
+
+ //stop in the uncaught exception
+ CheckLocation(debugger_test_loc, 28, 16, scripts, pause_location["callFrames"][0]["location"]);
+
+ await CheckValue(pause_location["data"], JObject.FromObject(new
+ {
+ type = "object",
+ subtype = "error",
+ className = "DebuggerTests.CustomException",
+ uncaught = true
+ }), "exception1.data");
+
+ var exception_members = await GetProperties(pause_location["data"]["objectId"]?.Value<string>());
+ CheckString(exception_members, "message", "not implemented uncaught");
+ }
+
+ [Fact]
+ public async Task ExceptionTestAllWithReload()
+ {
+ string entry_method_name = "[debugger-test] DebuggerTests.ExceptionTestsClass:TestExceptions";
+ var debugger_test_loc = "dotnet://debugger-test.dll/debugger-exception-test.cs";
+
+ await SetPauseOnException("all");
+
+ await SendCommand("Page.enable", null);
+ var pause_location = await SendCommandAndCheck(JObject.FromObject(new
+ {
+ ignoreCache = true
+ }), "Page.reload",null, 0, 0, null);
+ Thread.Sleep(1000);
+
+ //send a lot of resumes to "skip" all the pauses on caught exception and completely reload the page
+ int i = 0;
+ while (i < 100)
+ {
+ Result res = await cli.SendCommand("Debugger.resume", null, token);
+ i++;
+ }
+
+
+ var eval_expr = "window.setTimeout(function() { invoke_static_method (" +
+ $"'{entry_method_name}'" +
+ "); }, 1);";
+
+ pause_location = await EvaluateAndCheck(eval_expr, null, 0, 0, null);
+ //stop in the managed caught exception
+ pause_location = await WaitForManagedException(pause_location);
+
+ AssertEqual("run", pause_location["callFrames"]?[0]?["functionName"]?.Value<string>(), "pause0");
+
+ await CheckValue(pause_location["data"], JObject.FromObject(new
+ {
+ type = "object",
+ subtype = "error",
+ className = "DebuggerTests.CustomException",
+ uncaught = false
+ }), "exception0.data");
+
+ var exception_members = await GetProperties(pause_location["data"]["objectId"]?.Value<string>());
+ CheckString(exception_members, "message", "not implemented caught");
+
+ pause_location = await WaitForManagedException(null);
+ AssertEqual("run", pause_location["callFrames"]?[0]?["functionName"]?.Value<string>(), "pause1");
+
+ //stop in the uncaught exception
+ CheckLocation(debugger_test_loc, 28, 16, scripts, pause_location["callFrames"][0]["location"]);
+
+ await CheckValue(pause_location["data"], JObject.FromObject(new
+ {
+ type = "object",
+ subtype = "error",
+ className = "DebuggerTests.CustomException",
+ uncaught = true
+ }), "exception1.data");
+
+ exception_members = await GetProperties(pause_location["data"]["objectId"]?.Value<string>());
+ CheckString(exception_members, "message", "not implemented uncaught");
+ }
+
+
async Task<JObject> WaitForManagedException(JObject pause_location)
{
while (true)