Update ws-proxy to match Mono commit 4c348d6567f42be (#19600)

This commit is contained in:
msftbot[bot] 2020-03-05 14:46:56 +00:00 committed by GitHub
parent f4446f373f
commit e5cd390a8c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 249 additions and 206 deletions

View File

@ -12,56 +12,76 @@ using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Threading;
using Microsoft.Extensions.Logging;
using System.Runtime.CompilerServices;
namespace WebAssembly.Net.Debugging {
internal class BreakpointRequest {
public string Id { get; private set; }
public string Assembly { get; private set; }
public string File { get; private set; }
public int Line { get; private set; }
public int Column { get; private set; }
JObject request;
public bool IsResolved => Assembly != null;
public List<Breakpoint> Locations { get; } = new List<Breakpoint> ();
public override string ToString () {
return $"BreakpointRequest Assembly: {Assembly} File: {File} Line: {Line} Column: {Column}";
}
public static BreakpointRequest Parse (JObject args, DebugStore store)
public object AsSetBreakpointByUrlResponse ()
=> new { breakpointId = Id, locations = Locations.Select(l => l.Location.AsLocation ()) };
public static BreakpointRequest Parse (string id, JObject args)
{
// Events can potentially come out of order, so DebugStore may not be initialized
// The BP being set in these cases are JS ones, which we can safely ignore
if (args == null || store == null)
return null;
var url = args? ["url"]?.Value<string> ();
if (url == null) {
var urlRegex = args?["urlRegex"].Value<string>();
var sourceFile = store?.GetFileByUrlRegex (urlRegex);
url = sourceFile?.DotNetUrl;
}
if (url != null && !url.StartsWith ("dotnet://", StringComparison.Ordinal)) {
var sourceFile = store.GetFileByUrl (url);
url = sourceFile?.DotNetUrl;
}
if (url == null)
return null;
var parts = ParseDocumentUrl (url);
if (parts.Assembly == null)
return null;
var line = args? ["lineNumber"]?.Value<int> ();
var column = args? ["columnNumber"]?.Value<int> ();
if (line == null || column == null)
return null;
return new BreakpointRequest () {
Assembly = parts.Assembly,
File = parts.DocumentPath,
Line = line.Value,
Column = column.Value
var breakRequest = new BreakpointRequest () {
Id = id,
request = args
};
return breakRequest;
}
public BreakpointRequest Clone ()
=> new BreakpointRequest { Id = Id, request = request };
public bool IsMatch (SourceFile sourceFile)
{
var url = request? ["url"]?.Value<string> ();
if (url == null) {
var urlRegex = request?["urlRegex"].Value<string>();
var regex = new Regex (urlRegex);
return regex.IsMatch (sourceFile.Url.ToString ()) || regex.IsMatch (sourceFile.DocUrl);
}
return sourceFile.Url.ToString () == url || sourceFile.DotNetUrl == url;
}
public bool TryResolve (SourceFile sourceFile)
{
if (!IsMatch (sourceFile))
return false;
var line = request? ["lineNumber"]?.Value<int> ();
var column = request? ["columnNumber"]?.Value<int> ();
if (line == null || column == null)
return false;
Assembly = sourceFile.AssemblyName;
File = sourceFile.DebuggerFileName;
Line = line.Value;
Column = column.Value;
return true;
}
public bool TryResolve (DebugStore store)
{
if (request == null || store == null)
return false;
return store.AllSources().FirstOrDefault (source => TryResolve (source)) != null;
}
static (string Assembly, string DocumentPath) ParseDocumentUrl (string url)
@ -99,7 +119,6 @@ namespace WebAssembly.Net.Debugging {
}
}
internal class CliLocation {
public CliLocation (MethodInfo method, int offset)
{
@ -111,7 +130,6 @@ namespace WebAssembly.Net.Debugging {
public int Offset { get; private set; }
}
internal class SourceLocation {
SourceId id;
int line;
@ -268,11 +286,26 @@ namespace WebAssembly.Net.Debugging {
this.source = source;
var sps = methodDef.DebugInformation.SequencePoints;
if (sps != null && sps.Count > 0) {
StartLocation = new SourceLocation (this, sps [0]);
EndLocation = new SourceLocation (this, sps [sps.Count - 1]);
if (sps == null || sps.Count() < 1)
return;
SequencePoint start = sps [0];
SequencePoint end = sps [0];
foreach (var sp in sps) {
if (sp.StartLine < start.StartLine)
start = sp;
else if (sp.StartLine == start.StartLine && sp.StartColumn < start.StartColumn)
start = sp;
if (sp.EndLine > end.EndLine)
end = sp;
else if (sp.EndLine == end.EndLine && sp.EndColumn > end.EndColumn)
end = sp;
}
StartLocation = new SourceLocation (this, start);
EndLocation = new SourceLocation (this, end);
}
public SourceLocation GetLocationByIl (int pos)
@ -556,7 +589,7 @@ namespace WebAssembly.Net.Debugging {
public Task<byte[][]> Data { get; set; }
}
public async Task Load (SessionId sessionId, string [] loaded_files, CancellationToken token)
public async IAsyncEnumerable<SourceFile> Load (SessionId sessionId, string [] loaded_files, [EnumeratorCancellation] CancellationToken token)
{
static bool MatchPdb (string asm, string pdb)
=> Path.ChangeExtension (asm, "pdb") == pdb;
@ -585,12 +618,19 @@ namespace WebAssembly.Net.Debugging {
}
foreach (var step in steps) {
AssemblyInfo assembly = null;
try {
var bytes = await step.Data;
assemblies.Add (new AssemblyInfo (step.Url, bytes[0], bytes[1]));
assembly = new AssemblyInfo (step.Url, bytes [0], bytes [1]);
} catch (Exception e) {
logger.LogDebug ($"Failed to Load {step.Url} ({e.Message})");
}
if (assembly == null)
continue;
assemblies.Add (assembly);
foreach (var source in assembly.Sources)
yield return source;
}
}
@ -598,7 +638,7 @@ namespace WebAssembly.Net.Debugging {
=> assemblies.SelectMany (a => a.Sources);
public SourceFile GetFileById (SourceId id)
=> AllSources ().FirstOrDefault (f => f.SourceId.Equals (id));
=> AllSources ().SingleOrDefault (f => f.SourceId.Equals (id));
public AssemblyInfo GetAssemblyByName (string name)
=> assemblies.FirstOrDefault (a => a.Name.Equals (name, StringComparison.InvariantCultureIgnoreCase));
@ -612,15 +652,16 @@ namespace WebAssembly.Net.Debugging {
var spStart = (Line: sp.StartLine - 1, Column: sp.StartColumn - 1);
var spEnd = (Line: sp.EndLine - 1, Column: sp.EndColumn - 1);
if (start.Line > spStart.Line)
return false;
if (start.Column > spStart.Column && start.Line == sp.StartLine)
if (start.Line > spEnd.Line)
return false;
if (end.Line < spEnd.Line)
if (start.Column > spEnd.Column && start.Line == spEnd.Line)
return false;
if (end.Column < spEnd.Column && end.Line == spEnd.Line)
if (end.Line < spStart.Line)
return false;
if (end.Column < spStart.Column && end.Line == spStart.Line)
return false;
return true;
@ -629,22 +670,25 @@ namespace WebAssembly.Net.Debugging {
public List<SourceLocation> FindPossibleBreakpoints (SourceLocation start, SourceLocation end)
{
//XXX FIXME no idea what todo with locations on different files
if (start.Id != end.Id)
if (start.Id != end.Id) {
logger.LogDebug ($"FindPossibleBreakpoints: documents differ (start: {start.Id}) (end {end.Id}");
return null;
var src_id = start.Id;
}
var doc = GetFileById (src_id);
var sourceId = start.Id;
var doc = GetFileById (sourceId);
var res = new List<SourceLocation> ();
if (doc == null) {
logger.LogDebug ($"Could not find document {src_id}");
logger.LogDebug ($"Could not find document {sourceId}");
return res;
}
foreach (var m in doc.Methods) {
foreach (var sp in m.methodDef.DebugInformation.SequencePoints) {
if (Match (sp, start, end))
res.Add (new SourceLocation (m, sp));
foreach (var method in doc.Methods) {
foreach (var sequencePoint in method.methodDef.DebugInformation.SequencePoints) {
if (!sequencePoint.IsHidden && Match (sequencePoint, start, end))
res.Add (new SourceLocation (method, sequencePoint));
}
}
return res;
@ -674,35 +718,26 @@ namespace WebAssembly.Net.Debugging {
return true;
}
public SourceLocation FindBestBreakpoint (BreakpointRequest req)
public IEnumerable<SourceLocation> FindBreakpointLocations (BreakpointRequest request)
{
var asm = assemblies.FirstOrDefault (a => a.Name.Equals (req.Assembly, StringComparison.OrdinalIgnoreCase));
var src = asm?.Sources?.FirstOrDefault (s => s.DebuggerFileName.Equals (req.File, StringComparison.OrdinalIgnoreCase));
request.TryResolve (this);
if (src == null)
return null;
var asm = assemblies.FirstOrDefault (a => a.Name.Equals (request.Assembly, StringComparison.OrdinalIgnoreCase));
var sourceFile = asm?.Sources?.SingleOrDefault (s => s.DebuggerFileName.Equals (request.File, StringComparison.OrdinalIgnoreCase));
foreach (var m in src.Methods) {
foreach (var sp in m.methodDef.DebugInformation.SequencePoints) {
if (sourceFile == null)
yield break;
foreach (var method in sourceFile.Methods) {
foreach (var sequencePoint in method.methodDef.DebugInformation.SequencePoints) {
//FIXME handle multi doc methods
if (Match (sp, req.Line, req.Column))
return new SourceLocation (m, sp);
if (!sequencePoint.IsHidden && Match (sequencePoint, request.Line, request.Column))
yield return new SourceLocation (method, sequencePoint);
}
}
return null;
}
public string ToUrl (SourceLocation location)
=> location != null ? GetFileById (location.Id).Url : "";
public SourceFile GetFileByUrlRegex (string urlRegex)
{
var regex = new Regex (urlRegex);
return AllSources ().FirstOrDefault (file => regex.IsMatch (file.Url.ToString()) || regex.IsMatch (file.DocUrl));
}
public SourceFile GetFileByUrl (string url)
=> AllSources ().FirstOrDefault (file => file.Url.ToString() == url);
}
}

View File

@ -290,13 +290,14 @@ namespace WebAssembly.Net.Debugging {
internal void SendResponse (MessageId id, Result result, CancellationToken token)
{
//Log ("verbose", $"sending response: {id}: {result.ToJObject (id)}");
SendResponseInternal (id, result, token);
}
void SendResponseInternal (MessageId id, Result result, CancellationToken token)
{
JObject o = result.ToJObject (id);
if (result.IsErr)
logger.LogError ("sending error response {result}", result);
Send (this.ide, o, token);
}

View File

@ -75,10 +75,9 @@ namespace WebAssembly.Net.Debugging {
class Breakpoint {
public SourceLocation Location { get; private set; }
public int LocalId { get; private set; }
public int RemoteId { get; set; }
public BreakpointState State { get; set; }
public string StackId => $"dotnet:{LocalId}";
public string StackId { get; private set; }
public static bool TryParseId (string stackId, out int id)
{
@ -89,10 +88,10 @@ namespace WebAssembly.Net.Debugging {
return int.TryParse (stackId.Substring ("dotnet:".Length), out id);
}
public Breakpoint (SourceLocation loc, int localId, BreakpointState state)
public Breakpoint (string stackId, SourceLocation loc, BreakpointState state)
{
this.StackId = stackId;
this.Location = loc;
this.LocalId = localId;
this.State = state;
}
}
@ -110,18 +109,17 @@ namespace WebAssembly.Net.Debugging {
}
internal class ExecutionContext {
int breakpointIndex = -1;
public List<Breakpoint> Breakpoints { get; } = new List<Breakpoint> ();
public string DebuggerId { get; set; }
public Dictionary<string,BreakpointRequest> BreakpointRequests { get; } = new Dictionary<string,BreakpointRequest> ();
public TaskCompletionSource<DebugStore> ready = null;
public bool IsRuntimeReady => ready != null && ready.Task.IsCompleted;
public bool RuntimeReady { get; set; }
public int Id { get; set; }
public object AuxData { get; set; }
public List<Frame> CallStack { get; set; }
public int NextBreakpointId ()
=> Interlocked.Increment (ref breakpointIndex);
internal DebugStore store;
public TaskCompletionSource<DebugStore> Source { get; } = new TaskCompletionSource<DebugStore> ();
@ -149,6 +147,14 @@ namespace WebAssembly.Net.Debugging {
throw new ArgumentException ($"Invalid Session: \"{id}\"", nameof (sessionId));
}
bool UpdateContext (SessionId sessionId, ExecutionContext executionContext, out ExecutionContext previousExecutionContext)
{
var id = sessionId?.sessionId ?? "default";
var previous = contexts.TryGetValue (id, out previousExecutionContext);
contexts[id] = executionContext;
return previous;
}
internal Task<Result> SendMonoCommand (SessionId id, MonoCommands cmd, CancellationToken token)
=> SendCommand (id, "Runtime.evaluate", JObject.FromObject (cmd), token);
@ -163,6 +169,7 @@ namespace WebAssembly.Net.Debugging {
}
break;
}
case "Runtime.executionContextCreated": {
SendEvent (sessionId, method, args, token);
var ctx = args? ["context"];
@ -187,27 +194,47 @@ namespace WebAssembly.Net.Debugging {
break;
}
case "Debugger.breakpointResolved": {
break;
}
case "Debugger.scriptParsed":{
var url = args? ["url"]?.Value<string> () ?? "";
switch (url) {
case var _ when url == "":
case var _ when url.StartsWith ("wasm://", StringComparison.Ordinal): {
Log ("info", $"ignoring wasm: Debugger.scriptParsed {url}");
Log ("verbose", $"ignoring wasm: Debugger.scriptParsed {url}");
return true;
}
}
Log ("info", $"proxying Debugger.scriptParsed ({sessionId.sessionId}) {url} {args}");
Log ("verbose", $"proxying Debugger.scriptParsed ({sessionId.sessionId}) {url} {args}");
break;
}
}
return false;
}
async Task<bool> IsRuntimeAlreadyReadyAlready (SessionId sessionId, CancellationToken token)
{
var res = await SendMonoCommand (sessionId, MonoCommands.IsRuntimeReady (), token);
return res.Value? ["result"]? ["value"]?.Value<bool> () ?? false;
}
protected override async Task<bool> AcceptCommand (MessageId id, string method, JObject args, CancellationToken token)
{
switch (method) {
case "Debugger.enable": {
var resp = await SendCommand (id, method, args, token);
GetContext (id).DebuggerId = resp.Value ["debuggerId"]?.ToString ();
if (await IsRuntimeAlreadyReadyAlready (id, token))
await RuntimeReady (id, token);
SendResponse (id,resp,token);
return true;
}
case "Debugger.getScriptSource": {
var script = args? ["scriptId"]?.Value<string> ();
@ -240,19 +267,27 @@ namespace WebAssembly.Net.Debugging {
return true;
}
case "Debugger.setBreakpoint": {
break;
}
case "Debugger.setBreakpointByUrl": {
var context = GetContext (id);
var resp = await SendCommand (id, method, args, token);
if (resp.IsOk && resp.Value ["locations"].HasValues) {
if (!resp.IsOk) {
SendResponse (id, resp, token);
return true;
}
Log ("info", $"BP req {args}");
var bp_req = BreakpointRequest.Parse (args, GetContext (id).Store);
if (bp_req != null && await SetBreakpoint (id, bp_req, token))
return true;
var bpid = resp.Value["breakpointId"]?.ToString ();
var request = BreakpointRequest.Parse (bpid, args);
context.BreakpointRequests[bpid] = request;
var store = await RuntimeReady (id, token);
SendResponse (id, resp, token);
Log ("verbose", $"BP req {args}");
await SetBreakpoint (id, store, request, token);
SendResponse (id, Result.OkFromObject (request.AsSetBreakpointByUrlResponse()), token);
return true;
}
@ -333,10 +368,8 @@ namespace WebAssembly.Net.Debugging {
//Give up and send the original call stack
return false;
}
var bp = context.Breakpoints.FirstOrDefault (b => b.RemoteId == bp_id.Value);
var store = context.Store;
var src = bp == null ? null : store.GetFileById (bp.Location.Id);
var bp = context.BreakpointRequests.Values.SelectMany (v => v.Locations).FirstOrDefault (b => b.RemoteId == bp_id.Value);
var callFrames = new List<object> ();
foreach (var frame in orig_callframes) {
@ -352,6 +385,7 @@ namespace WebAssembly.Net.Debugging {
var method_token = mono_frame ["method_token"].Value<int> ();
var assembly_name = mono_frame ["assembly_name"].Value<string> ();
var store = await LoadStore (sessionId, token);
var asm = store.GetAssemblyByName (assembly_name);
if (asm == null) {
Log ("info",$"Unable to find assembly: {assembly_name}");
@ -430,25 +464,19 @@ namespace WebAssembly.Net.Debugging {
async Task OnDefaultContext (SessionId sessionId, ExecutionContext context, CancellationToken token)
{
Log ("verbose", "Default context created, clearing state and sending events");
contexts[sessionId.sessionId ?? "default"] = context;
//reset all bps
foreach (var b in context.Breakpoints){
b.State = BreakpointState.Pending;
if (UpdateContext (sessionId, context, out var previousContext)) {
foreach (var kvp in previousContext.BreakpointRequests) {
context.BreakpointRequests[kvp.Key] = kvp.Value.Clone();
}
}
Log ("debug", "checking if the runtime is ready");
var res = await SendMonoCommand (sessionId, MonoCommands.IsRuntimeReady (), token);
var is_ready = res.Value? ["result"]? ["value"]?.Value<bool> ();
if (is_ready.HasValue && is_ready.Value == true) {
if (await IsRuntimeAlreadyReadyAlready (sessionId, token))
await RuntimeReady (sessionId, token);
}
}
async Task OnResume (MessageId msd_id, CancellationToken token)
{
//discard frames
//discard managed frames
GetContext (msd_id).CallStack = null;
await Task.CompletedTask;
}
@ -487,8 +515,7 @@ namespace WebAssembly.Net.Debugging {
var res = await SendMonoCommand(msg_id, cmd, token);
//if we fail we just buble that to the IDE (and let it panic over it)
if (res.IsErr)
{
if (res.IsErr) {
SendResponse(msg_id, res, token);
return;
}
@ -544,8 +571,10 @@ namespace WebAssembly.Net.Debugging {
var values = res.Value? ["result"]? ["value"]?.Values<JObject> ().ToArray ();
if(values == null)
if(values == null) {
SendResponse (msg_id, Result.OkFromObject (new {result = Array.Empty<object> ()}), token);
return;
}
var var_list = new List<object> ();
int i = 0;
@ -589,8 +618,9 @@ namespace WebAssembly.Net.Debugging {
}
}
async Task<Result> EnableBreakpoint (SessionId sessionId, Breakpoint bp, CancellationToken token)
async Task<Breakpoint> SetMonoBreakpoint (SessionId sessionId, BreakpointRequest req, SourceLocation location, CancellationToken token)
{
var bp = new Breakpoint (req.Id, location, BreakpointState.Pending);
var asm_name = bp.Location.CliLocation.Method.Assembly.Name;
var method_token = bp.Location.CliLocation.Method.Token;
var il_offset = bp.Location.CliLocation.Offset;
@ -604,147 +634,124 @@ namespace WebAssembly.Net.Debugging {
//Log ("verbose", $"BP local id {bp.LocalId} enabled with remote id {bp.RemoteId}");
}
return res;
return bp;
}
async Task<DebugStore> LoadStore (SessionId sessionId, CancellationToken token)
{
var context = GetContext (sessionId);
if (Interlocked.CompareExchange (ref context.store, new DebugStore (logger), null) != null) {
if (Interlocked.CompareExchange (ref context.store, new DebugStore (logger), null) != null)
return await context.Source.Task;
}
try {
var loaded_pdbs = await SendMonoCommand (sessionId, MonoCommands.GetLoadedFiles(), token);
var the_value = loaded_pdbs.Value? ["result"]? ["value"];
var the_pdbs = the_value?.ToObject<string[]> ();
await context.store.Load(sessionId, the_pdbs, token);
await foreach (var source in context.store.Load(sessionId, the_pdbs, token).WithCancellation (token)) {
var scriptSource = JObject.FromObject (source.ToScriptSource (context.Id, context.AuxData));
Log ("verbose", $"\tsending {source.Url} {context.Id} {sessionId.sessionId}");
SendEvent (sessionId, "Debugger.scriptParsed", scriptSource, token);
foreach (var req in context.BreakpointRequests.Values) {
if (req.TryResolve (source)) {
await SetBreakpoint (sessionId, context.store, req, token);
}
}
}
} catch (Exception e) {
context.Source.SetException (e);
}
if (!context.Source.Task.IsCompleted)
context.Source.SetResult (context.store);
return await context.Source.Task;
return context.store;
}
async Task RuntimeReady (SessionId sessionId, CancellationToken token)
async Task<DebugStore> RuntimeReady (SessionId sessionId, CancellationToken token)
{
var context = GetContext (sessionId);
if (context.RuntimeReady)
return;
if (Interlocked.CompareExchange (ref context.ready, new TaskCompletionSource<DebugStore> (), null) != null)
return await context.ready.Task;
var clear_result = await SendMonoCommand (sessionId, MonoCommands.ClearAllBreakpoints (), token);
if (clear_result.IsErr) {
Log ("verbose", $"Failed to clear breakpoints due to {clear_result}");
}
context.RuntimeReady = true;
var store = await LoadStore (sessionId, token);
foreach (var s in store.AllSources ()) {
var scriptSource = JObject.FromObject (s.ToScriptSource (context.Id, context.AuxData));
Log ("verbose", $"\tsending {s.Url} {context.Id} {sessionId.sessionId}");
SendEvent (sessionId, "Debugger.scriptParsed", scriptSource, token);
}
foreach (var bp in context.Breakpoints) {
if (bp.State != BreakpointState.Pending)
continue;
var res = await EnableBreakpoint (sessionId, bp, token);
var ret_code = res.Value? ["result"]? ["value"]?.Value<int> ();
//if we fail we just buble that to the IDE (and let it panic over it)
if (!ret_code.HasValue) {
//FIXME figure out how to inform the IDE of that.
Log ("info", $"FAILED TO ENABLE BP {bp.LocalId}");
bp.State = BreakpointState.Disabled;
}
}
context.ready.SetResult (store);
SendEvent (sessionId, "Mono.runtimeReady", new JObject (), token);
return store;
}
async Task<bool> RemoveBreakpoint(MessageId msg_id, JObject args, CancellationToken token) {
var bpid = args? ["breakpointId"]?.Value<string> ();
if (!Breakpoint.TryParseId (bpid, out var the_id))
return false;
var context = GetContext (msg_id);
var bp = context.Breakpoints.FirstOrDefault (b => b.LocalId == the_id);
if (bp == null) {
Log ("info", $"Could not find dotnet bp with id {the_id}");
if (!context.BreakpointRequests.TryGetValue (bpid, out var breakpointRequest))
return false;
}
context.Breakpoints.Remove (bp);
//FIXME verify result (and log?)
var res = await RemoveBreakpoint (msg_id, bp, token);
return true;
}
async Task<Result> RemoveBreakpoint (SessionId sessionId, Breakpoint bp, CancellationToken token)
{
var res = await SendMonoCommand (sessionId, MonoCommands.RemoveBreakpoint (bp.RemoteId), token);
var ret_code = res.Value? ["result"]? ["value"]?.Value<int> ();
if (ret_code.HasValue) {
bp.RemoteId = -1;
bp.State = BreakpointState.Disabled;
}
return res;
}
async Task<bool> SetBreakpoint (MessageId msg_id, BreakpointRequest req, CancellationToken token)
{
var context = GetContext (msg_id);
var bp_loc = context.Store.FindBestBreakpoint (req);
Log ("info", $"BP request for '{req}' runtime ready {context.RuntimeReady} location '{bp_loc}'");
if (bp_loc == null) {
Log ("verbose", $"Could not resolve breakpoint request: {req}");
return false;
}
Breakpoint bp = null;
if (!context.RuntimeReady) {
bp = new Breakpoint (bp_loc, context.NextBreakpointId (), BreakpointState.Pending);
} else {
bp = new Breakpoint (bp_loc, context.NextBreakpointId (), BreakpointState.Disabled);
var res = await EnableBreakpoint (msg_id, bp, token);
foreach (var bp in breakpointRequest.Locations) {
var res = await SendMonoCommand (msg_id, MonoCommands.RemoveBreakpoint (bp.RemoteId), token);
var ret_code = res.Value? ["result"]? ["value"]?.Value<int> ();
//if we fail we just buble that to the IDE (and let it panic over it)
if (!ret_code.HasValue) {
return false;
if (ret_code.HasValue) {
bp.RemoteId = -1;
bp.State = BreakpointState.Disabled;
}
}
context.Breakpoints.Add (bp);
var ok = new {
breakpointId = bp.StackId,
locations = new [] {
bp_loc.AsLocation ()
},
};
SendResponse (msg_id, Result.OkFromObject (ok), token);
return true;
breakpointRequest.Locations.Clear ();
return false;
}
async Task<bool> GetPossibleBreakpoints (MessageId msg_id, SourceLocation start, SourceLocation end, CancellationToken token)
async Task SetBreakpoint (SessionId sessionId, DebugStore store, BreakpointRequest req, CancellationToken token)
{
var bps = (await LoadStore (msg_id, token)).FindPossibleBreakpoints (start, end);
var context = GetContext (sessionId);
if (req.Locations.Any ()) {
Log ("debug", $"locations already loaded for {req.Id}");
return;
}
var locations = store.FindBreakpointLocations (req).ToList ();
logger.LogDebug ("BP request for '{req}' runtime ready {context.RuntimeReady}", req, GetContext (sessionId).IsRuntimeReady);
var breakpoints = new List<Breakpoint> ();
foreach (var loc in locations) {
var bp = await SetMonoBreakpoint (sessionId, req, loc, token);
// If we didn't successfully enable the breakpoint
// don't add it to the list of locations for this id
if (bp.State != BreakpointState.Active)
continue;
breakpoints.Add (bp);
var resolvedLocation = new {
breakpointId = req.Id,
location = loc.AsLocation ()
};
SendEvent (sessionId, "Debugger.breakpointResolved", JObject.FromObject (resolvedLocation), token);
}
req.Locations.AddRange (breakpoints);
return;
}
async Task<bool> GetPossibleBreakpoints (MessageId msg, SourceLocation start, SourceLocation end, CancellationToken token)
{
var bps = (await RuntimeReady (msg, token)).FindPossibleBreakpoints (start, end);
if (bps == null)
return false;
SendResponse (msg_id, Result.OkFromObject (new { locations = bps.Select (b => b.AsLocation ()) }), token);
var response = new { locations = bps.Select (b => b.AsLocation ()) };
SendResponse (msg, Result.OkFromObject (response), token);
return true;
}