diff --git a/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/DebugStore.cs b/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/DebugStore.cs
index ee28bd94b7..e1e9b7392b 100644
--- a/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/DebugStore.cs
+++ b/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/DebugStore.cs
@@ -545,7 +545,7 @@ namespace WsProxy {
return assemblies.FirstOrDefault (a => a.Name.Equals (name, StringComparison.InvariantCultureIgnoreCase));
}
- /*
+ /*
V8 uses zero based indexing for both line and column.
PPDBs uses one based indexing for both line and column.
*/
@@ -598,7 +598,7 @@ namespace WsProxy {
PPDBs uses one based indexing for both line and column.
*/
static bool Match (SequencePoint sp, int line, int column)
- {
+ {
var bp = (line: line + 1, column: column + 1);
if (sp.StartLine > bp.line || sp.EndLine < bp.line)
diff --git a/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/MonoProxy.cs b/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/MonoProxy.cs
index 8c440da1ce..dfcb373afe 100644
--- a/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/MonoProxy.cs
+++ b/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/MonoProxy.cs
@@ -223,7 +223,7 @@ namespace WsProxy {
Info ("RUNTIME READY, PARTY TIME");
await RuntimeReady (token);
await SendCommand ("Debugger.resume", new JObject (), token);
- SendEvent ("Mono.runtimeReady", new JObject (), token);
+ SendEvent ("Mono.runtimeReady", new JObject (), token);
}
async Task OnBreakPointHit (JObject args, CancellationToken token)
@@ -274,12 +274,18 @@ namespace WsProxy {
var frames = new List ();
int frame_id = 0;
var the_mono_frames = res.Value? ["result"]? ["value"]? ["frames"]?.Values ();
+
foreach (var mono_frame in the_mono_frames) {
var il_pos = mono_frame ["il_pos"].Value ();
var method_token = mono_frame ["method_token"].Value ();
var assembly_name = mono_frame ["assembly_name"].Value ();
var asm = store.GetAssemblyByName (assembly_name);
+ if (asm == null) {
+ Info ($"Unable to find assembly: {assembly_name}");
+ continue;
+ }
+
var method = asm.GetMethodByToken (method_token);
if (method == null) {
@@ -374,7 +380,7 @@ namespace WsProxy {
//Debug ($"\t{is_ready}");
if (is_ready.HasValue && is_ready.Value == true) {
Debug ("RUNTIME LOOK READY. GO TIME!");
- await RuntimeReady (token);
+ await OnRuntimeReady (token);
}
}
@@ -426,33 +432,38 @@ namespace WsProxy {
return;
}
- var values = res.Value?["result"]?["value"]?.Values().ToArray();
+ try {
+ var values = res.Value?["result"]?["value"]?.Values().ToArray() ?? Array.Empty();
+ var var_list = new List();
- var var_list = new List();
+ // Trying to inspect the stack frame for DotNetDispatcher::InvokeSynchronously
+ // results in a "Memory access out of bounds", causing 'values' to be null,
+ // so skip returning variable values in that case.
+ for (int i = 0; i < values.Length; i+=2)
+ {
+ string fieldName = (string)values[i]["name"];
+ if (fieldName.Contains("k__BackingField")){
+ fieldName = fieldName.Replace("k__BackingField", "");
+ fieldName = fieldName.Replace("<", "");
+ fieldName = fieldName.Replace(">", "");
+ }
+ var value = values [i + 1]? ["value"];
+ if (((string)value ["description"]) == null)
+ value ["description"] = value ["value"]?.ToString ();
- // Trying to inspect the stack frame for DotNetDispatcher::InvokeSynchronously
- // results in a "Memory access out of bounds", causing 'values' to be null,
- // so skip returning variable values in that case.
- for (int i = 0; i < values.Length; i+=2)
- {
- string fieldName = (string)values[i]["name"];
- if (fieldName.Contains("k__BackingField")){
- fieldName = fieldName.Replace("k__BackingField", "");
- fieldName = fieldName.Replace("<", "");
- fieldName = fieldName.Replace(">", "");
+ var_list.Add(JObject.FromObject(new {
+ name = fieldName,
+ value
+ }));
+
+ }
+ o = JObject.FromObject(new
+ {
+ result = var_list
+ });
+ } catch (Exception e) {
+ Debug ($"failed to parse {res.Value}");
}
- var_list.Add(JObject.FromObject(new
- {
- name = fieldName,
- value = values[i+1]["value"]
- }));
-
- }
- o = JObject.FromObject(new
- {
- result = var_list
- });
-
SendResponse(msg_id, Result.Ok(o), token);
}
@@ -481,41 +492,51 @@ namespace WsProxy {
return;
}
- var values = res.Value? ["result"]? ["value"]?.Values ().ToArray ();
+ try {
+ var values = res.Value? ["result"]? ["value"]?.Values ().ToArray ();
- var var_list = new List ();
- int i = 0;
- // Trying to inspect the stack frame for DotNetDispatcher::InvokeSynchronously
- // results in a "Memory access out of bounds", causing 'values' to be null,
- // so skip returning variable values in that case.
- while (values != null && i < vars.Length && i < values.Length) {
- var value = values [i] ["value"];
- if (((string)value ["description"]) == null)
- value ["description"] = value ["value"]?.ToString();
+ var var_list = new List ();
+ int i = 0;
+ // Trying to inspect the stack frame for DotNetDispatcher::InvokeSynchronously
+ // results in a "Memory access out of bounds", causing 'values' to be null,
+ // so skip returning variable values in that case.
+ while (values != null && i < vars.Length && i < values.Length) {
+ var value = values [i] ["value"];
+ if (((string)value ["description"]) == null)
+ value ["description"] = value ["value"]?.ToString ();
- var_list.Add (JObject.FromObject (new {
- name = vars [i].Name,
- value = values [i] ["value"]
- }));
- i++;
+ var_list.Add (JObject.FromObject (new {
+ name = vars [i].Name,
+ value
+ }));
+ i++;
+ }
+ //Async methods are special in the way that local variables can be lifted to generated class fields
+ //value of "this" comes here either
+ while (i < values.Length) {
+ String name = values [i] ["name"].ToString ();
+
+ if (name.IndexOf (">", StringComparison.Ordinal) > 0)
+ name = name.Substring (1, name.IndexOf (">", StringComparison.Ordinal) - 1);
+
+ var value = values [i + 1] ["value"];
+ if (((string)value ["description"]) == null)
+ value ["description"] = value ["value"]?.ToString ();
+
+ var_list.Add (JObject.FromObject (new {
+ name,
+ value
+ }));
+ i = i + 2;
+ }
+ o = JObject.FromObject (new {
+ result = var_list
+ });
+ SendResponse (msg_id, Result.Ok (o), token);
}
- //Async methods are special in the way that local variables can be lifted to generated class fields
- //value of "this" comes here either
- while (i < values.Length) {
- String name = values [i] ["name"].ToString ();
-
- if (name.IndexOf (">", StringComparison.Ordinal) > 0)
- name = name.Substring (1, name.IndexOf (">", StringComparison.Ordinal) - 1);
- var_list.Add (JObject.FromObject (new {
- name = name,
- value = values [i+1] ["value"]
- }));
- i = i + 2;
+ catch (Exception exc) {
+ SendResponse (msg_id, res, token);
}
- o = JObject.FromObject (new {
- result = var_list
- });
- SendResponse (msg_id, Result.Ok (o), token);
}
async Task EnableBreakPoint (Breakpoint bp, CancellationToken token)
diff --git a/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/WsProxy.cs b/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/WsProxy.cs
index 17c72e9ce7..87ef23027e 100644
--- a/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/WsProxy.cs
+++ b/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/WsProxy.cs
@@ -97,6 +97,7 @@ namespace WsProxy {
internal class WsProxy {
TaskCompletionSource side_exception = new TaskCompletionSource ();
+ TaskCompletionSource client_initiated_close = new TaskCompletionSource ();
List<(int, TaskCompletionSource)> pending_cmds = new List<(int, TaskCompletionSource)> ();
ClientWebSocket browser;
WebSocket ide;
@@ -119,8 +120,16 @@ namespace WsProxy {
byte [] buff = new byte [4000];
var mem = new MemoryStream ();
while (true) {
+
+ if (socket.State != WebSocketState.Open) {
+ Console.WriteLine ($"WSProxy: Socket is no longer open.");
+ client_initiated_close.TrySetResult (true);
+ return null;
+ }
+
var result = await socket.ReceiveAsync (new ArraySegment (buff), token);
if (result.MessageType == WebSocketMessageType.Close) {
+ client_initiated_close.TrySetResult (true);
return null;
}
@@ -144,7 +153,7 @@ namespace WsProxy {
void Send (WebSocket to, JObject o, CancellationToken token)
{
- var bytes = Encoding.UTF8.GetBytes (o.ToString ());
+ var bytes = Encoding.UTF8.GetBytes (o.ToString ());
var queue = GetQueueForSocket (to);
var task = queue.Send (bytes, token);
@@ -199,9 +208,10 @@ namespace WsProxy {
void ProcessIdeMessage (string msg, CancellationToken token)
{
- var res = JObject.Parse (msg);
-
- pending_ops.Add (OnCommand (res ["id"].Value (), res ["method"].Value (), res ["params"] as JObject, token));
+ if (!string.IsNullOrEmpty (msg)) {
+ var res = JObject.Parse (msg);
+ pending_ops.Add (OnCommand (res ["id"].Value (), res ["method"].Value (), res ["params"] as JObject, token));
+ }
}
internal async Task SendCommand (string method, JObject args, CancellationToken token) {
@@ -255,24 +265,25 @@ namespace WsProxy {
Send (this.ide, o, token);
}
- // , HttpContext context)
+ // , HttpContext context)
public async Task Run (Uri browserUri, WebSocket ideSocket)
{
- Debug ("wsproxy start");
+ Debug ($"WsProxy Starting on {browserUri}");
using (this.ide = ideSocket) {
- Debug ("ide connected");
+ Debug ($"WsProxy: IDE waiting for connection on {browserUri}");
queues.Add (new WsQueue (this.ide));
using (this.browser = new ClientWebSocket ()) {
this.browser.Options.KeepAliveInterval = Timeout.InfiniteTimeSpan;
await this.browser.ConnectAsync (browserUri, CancellationToken.None);
queues.Add (new WsQueue (this.browser));
- Debug ("client connected");
+ Debug ($"WsProxy: Client connected on {browserUri}");
var x = new CancellationTokenSource ();
pending_ops.Add (ReadOne (browser, x.Token));
pending_ops.Add (ReadOne (ide, x.Token));
pending_ops.Add (side_exception.Task);
+ pending_ops.Add (client_initiated_close.Task);
try {
while (!x.IsCancellationRequested) {
@@ -280,15 +291,23 @@ namespace WsProxy {
//Console.WriteLine ("pump {0} {1}", task, pending_ops.IndexOf (task));
if (task == pending_ops [0]) {
var msg = ((Task)task).Result;
- pending_ops [0] = ReadOne (browser, x.Token); //queue next read
- ProcessBrowserMessage (msg, x.Token);
+ if (msg != null) {
+ pending_ops [0] = ReadOne (browser, x.Token); //queue next read
+ ProcessBrowserMessage (msg, x.Token);
+ }
} else if (task == pending_ops [1]) {
var msg = ((Task)task).Result;
- pending_ops [1] = ReadOne (ide, x.Token); //queue next read
- ProcessIdeMessage (msg, x.Token);
+ if (msg != null) {
+ pending_ops [1] = ReadOne (ide, x.Token); //queue next read
+ ProcessIdeMessage (msg, x.Token);
+ }
} else if (task == pending_ops [2]) {
var res = ((Task)task).Result;
throw new Exception ("side task must always complete with an exception, what's going on???");
+ } else if (task == pending_ops [3]) {
+ var res = ((Task)task).Result;
+ Debug ($"WsProxy: Client initiated close from {browserUri}");
+ x.Cancel ();
} else {
//must be a background task
pending_ops.Remove (task);
@@ -301,10 +320,11 @@ namespace WsProxy {
}
}
} catch (Exception e) {
- Debug ($"got exception {e}");
+ Debug ($"WsProxy::Run: Exception {e}");
//throw;
} finally {
- x.Cancel ();
+ if (!x.IsCancellationRequested)
+ x.Cancel ();
}
}
}