Update DbgProxy to c8cbcaad (#20865)

This commit is contained in:
Pranav K 2020-04-20 11:22:29 -07:00 committed by GitHub
parent 089bb4a072
commit 2bd662fe0b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 284 additions and 140 deletions

View File

@ -13,6 +13,7 @@ using System.Threading.Tasks;
using System.Threading;
using Microsoft.Extensions.Logging;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
namespace WebAssembly.Net.Debugging {
internal class BreakpointRequest {
@ -21,26 +22,35 @@ namespace WebAssembly.Net.Debugging {
public string File { get; private set; }
public int Line { get; private set; }
public int Column { get; private set; }
public MethodInfo Method { 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 override string ToString ()
=> $"BreakpointRequest Assembly: {Assembly} File: {File} Line: {Line} Column: {Column}";
public object AsSetBreakpointByUrlResponse ()
=> new { breakpointId = Id, locations = Locations.Select(l => l.Location.AsLocation ()) };
public BreakpointRequest () {
}
public BreakpointRequest (string id, MethodInfo method) {
Id = id;
Method = method;
}
public BreakpointRequest (string id, JObject request) {
Id = id;
this.request = request;
}
public static BreakpointRequest Parse (string id, JObject args)
{
var breakRequest = new BreakpointRequest () {
Id = id,
request = args
};
return breakRequest;
return new BreakpointRequest (id, args);
}
public BreakpointRequest Clone ()
@ -83,18 +93,6 @@ namespace WebAssembly.Net.Debugging {
return store.AllSources().FirstOrDefault (source => TryResolve (source)) != null;
}
static (string Assembly, string DocumentPath) ParseDocumentUrl (string url)
{
if (Uri.TryCreate (url, UriKind.Absolute, out var docUri) && docUri.Scheme == "dotnet") {
return (
docUri.Host,
docUri.PathAndQuery.Substring (1)
);
} else {
return (null, null);
}
}
}
internal class VarInfo {
@ -110,13 +108,11 @@ namespace WebAssembly.Net.Debugging {
this.Index = (p.Index + 1) * -1;
}
public string Name { get; private set; }
public int Index { get; private set; }
public string Name { get; }
public int Index { get; }
public override string ToString ()
{
return $"(var-info [{Index}] '{Name}')";
}
=> $"(var-info [{Index}] '{Name}')";
}
internal class CliLocation {
@ -126,8 +122,8 @@ namespace WebAssembly.Net.Debugging {
Offset = offset;
}
public MethodInfo Method { get; private set; }
public int Offset { get; private set; }
public MethodInfo Method { get; }
public int Offset { get; }
}
internal class SourceLocation {
@ -157,9 +153,7 @@ namespace WebAssembly.Net.Debugging {
public CliLocation CliLocation => this.cliLoc;
public override string ToString ()
{
return $"{id}:{Line}:{Column}";
}
=> $"{id}:{Line}:{Column}";
public static SourceLocation Parse (JObject obj)
{
@ -235,9 +229,7 @@ namespace WebAssembly.Net.Debugging {
}
public override string ToString ()
{
return $"{Scheme}{assembly}_{document}";
}
=> $"{Scheme}{assembly}_{document}";
public override bool Equals (object obj)
{
@ -248,44 +240,36 @@ namespace WebAssembly.Net.Debugging {
}
public override int GetHashCode ()
{
return this.assembly.GetHashCode () ^ this.document.GetHashCode ();
}
=> assembly.GetHashCode () ^ document.GetHashCode ();
public static bool operator == (SourceId a, SourceId b)
{
if ((object)a == null)
return (object)b == null;
return a.Equals (b);
}
=> ((object)a == null) ? (object)b == null : a.Equals (b);
public static bool operator != (SourceId a, SourceId b)
{
return !a.Equals (b);
}
=> !a.Equals (b);
}
internal class MethodInfo {
AssemblyInfo assembly;
internal MethodDefinition methodDef;
MethodDefinition methodDef;
SourceFile source;
public SourceId SourceId => source.SourceId;
public string Name => methodDef.Name;
public MethodDebugInformation DebugInformation => methodDef.DebugInformation;
public SourceLocation StartLocation { get; private set; }
public SourceLocation EndLocation { get; private set; }
public AssemblyInfo Assembly => assembly;
public int Token => (int)methodDef.MetadataToken.RID;
public SourceLocation StartLocation { get; }
public SourceLocation EndLocation { get; }
public AssemblyInfo Assembly { get; }
public uint Token => methodDef.MetadataToken.RID;
public MethodInfo (AssemblyInfo assembly, MethodDefinition methodDef, SourceFile source)
{
this.assembly = assembly;
this.Assembly = assembly;
this.methodDef = methodDef;
this.source = source;
var sps = methodDef.DebugInformation.SequencePoints;
var sps = DebugInformation.SequencePoints;
if (sps == null || sps.Count() < 1)
return;
@ -311,7 +295,7 @@ namespace WebAssembly.Net.Debugging {
public SourceLocation GetLocationByIl (int pos)
{
SequencePoint prev = null;
foreach (var sp in methodDef.DebugInformation.SequencePoints) {
foreach (var sp in DebugInformation.SequencePoints) {
if (sp.Offset > pos)
break;
prev = sp;
@ -328,7 +312,6 @@ namespace WebAssembly.Net.Debugging {
var res = new List<VarInfo> ();
res.AddRange (methodDef.Parameters.Select (p => new VarInfo (p)));
res.AddRange (methodDef.DebugInformation.GetScopes ()
.Where (s => s.Start.Offset <= offset && (s.End.IsEndOfMethod || s.End.Offset > offset))
.SelectMany (s => s.Variables)
@ -337,17 +320,38 @@ namespace WebAssembly.Net.Debugging {
return res.ToArray ();
}
public override string ToString () => "MethodInfo(" + methodDef.FullName + ")";
}
internal class AssemblyInfo {
internal class TypeInfo {
AssemblyInfo assembly;
TypeDefinition type;
List<MethodInfo> methods;
public TypeInfo (AssemblyInfo assembly, TypeDefinition type) {
this.assembly = assembly;
this.type = type;
methods = new List<MethodInfo> ();
}
public string Name => type.Name;
public string FullName => type.FullName;
public List<MethodInfo> Methods => methods;
public override string ToString () => "TypeInfo('" + FullName + "')";
}
class AssemblyInfo {
static int next_id;
ModuleDefinition image;
readonly int id;
readonly ILogger logger;
Dictionary<int, MethodInfo> methods = new Dictionary<int, MethodInfo> ();
Dictionary<uint, MethodInfo> methods = new Dictionary<uint, MethodInfo> ();
Dictionary<string, string> sourceLinkMappings = new Dictionary<string, string>();
Dictionary<string, TypeInfo> typesByName = new Dictionary<string, TypeInfo> ();
readonly List<SourceFile> sources = new List<SourceFile>();
internal string Url { get; private set; }
internal string Url { get; }
public AssemblyInfo (string url, byte[] assembly, byte[] pdb)
{
@ -356,18 +360,23 @@ namespace WebAssembly.Net.Debugging {
try {
Url = url;
ReaderParameters rp = new ReaderParameters (/*ReadingMode.Immediate*/);
// set ReadSymbols = true unconditionally in case there
// is an embedded pdb then handle ArgumentException
// and assume that if pdb == null that is the cause
rp.ReadSymbols = true;
rp.SymbolReaderProvider = new PdbReaderProvider ();
if (pdb != null)
rp.SymbolStream = new MemoryStream (pdb);
rp.ReadingMode = ReadingMode.Immediate;
rp.InMemory = true;
this.image = ModuleDefinition.ReadModule (new MemoryStream (assembly), rp);
} catch (BadImageFormatException ex) {
logger.LogWarning ($"Failed to read assembly as portable PDB: {ex.Message}");
} catch (ArgumentNullException) {
} catch (ArgumentException) {
// if pdb == null this is expected and we
// read the assembly without symbols below
if (pdb != null)
throw;
}
@ -396,45 +405,38 @@ namespace WebAssembly.Net.Debugging {
void Populate ()
{
ProcessSourceLink();
ProcessSourceLink();
var d2s = new Dictionary<Document, SourceFile> ();
Func<Document, SourceFile> get_src = (doc) => {
SourceFile FindSource (Document doc)
{
if (doc == null)
return null;
if (d2s.ContainsKey (doc))
return d2s [doc];
if (d2s.TryGetValue (doc, out var source))
return source;
var src = new SourceFile (this, sources.Count, doc, GetSourceLinkUrl (doc.Url));
sources.Add (src);
d2s [doc] = src;
return src;
};
foreach (var m in image.GetTypes().SelectMany(t => t.Methods)) {
Document first_doc = null;
foreach (var sp in m.DebugInformation.SequencePoints) {
if (first_doc == null && !sp.Document.Url.EndsWith (".g.cs", StringComparison.OrdinalIgnoreCase)) {
first_doc = sp.Document;
foreach (var type in image.GetTypes()) {
var typeInfo = new TypeInfo (this, type);
typesByName [type.FullName] = typeInfo;
foreach (var method in type.Methods) {
foreach (var sp in method.DebugInformation.SequencePoints) {
var source = FindSource (sp.Document);
var methodInfo = new MethodInfo (this, method, source);
methods [method.MetadataToken.RID] = methodInfo;
if (source != null)
source.AddMethod (methodInfo);
typeInfo.Methods.Add (methodInfo);
}
// else if (first_doc != sp.Document) {
// //FIXME this is needed for (c)ctors in corlib
// throw new Exception ($"Cant handle multi-doc methods in {m}");
//}
}
if (first_doc == null) {
// all generated files
first_doc = m.DebugInformation.SequencePoints.FirstOrDefault ()?.Document;
}
if (first_doc != null) {
var src = get_src (first_doc);
var mi = new MethodInfo (this, m, src);
int mt = (int)m.MetadataToken.RID;
this.methods [mt] = mi;
if (src != null)
src.AddMethod (mi);
}
}
}
@ -455,9 +457,8 @@ namespace WebAssembly.Net.Debugging {
private Uri GetSourceLinkUrl (string document)
{
if (sourceLinkMappings.TryGetValue (document, out string url)) {
if (sourceLinkMappings.TryGetValue (document, out string url))
return new Uri (url);
}
foreach (var sourceLinkDocument in sourceLinkMappings) {
string key = sourceLinkDocument.Key;
@ -477,7 +478,7 @@ namespace WebAssembly.Net.Debugging {
return null;
}
private string GetRelativePath (string relativeTo, string path)
private static string GetRelativePath (string relativeTo, string path)
{
var uri = new Uri (relativeTo, UriKind.RelativeOrAbsolute);
var rel = Uri.UnescapeDataString (uri.MakeRelativeUri (new Uri (path, UriKind.RelativeOrAbsolute)).ToString ()).Replace (Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
@ -487,9 +488,8 @@ namespace WebAssembly.Net.Debugging {
return rel;
}
public IEnumerable<SourceFile> Sources {
get { return this.sources; }
}
public IEnumerable<SourceFile> Sources
=> this.sources;
public int Id => id;
public string Name => image.Name;
@ -499,22 +499,27 @@ namespace WebAssembly.Net.Debugging {
return sources.FirstOrDefault (s => s.SourceId.Document == document);
}
public MethodInfo GetMethodByToken (int token)
public MethodInfo GetMethodByToken (uint token)
{
methods.TryGetValue (token, out var value);
return value;
}
public TypeInfo GetTypeByName (string name) {
typesByName.TryGetValue (name, out var res);
return res;
}
}
internal class SourceFile {
HashSet<MethodInfo> methods;
Dictionary<uint, MethodInfo> methods;
AssemblyInfo assembly;
int id;
Document doc;
internal SourceFile (AssemblyInfo assembly, int id, Document doc, Uri sourceLinkUri)
{
this.methods = new HashSet<MethodInfo> ();
this.methods = new Dictionary<uint, MethodInfo> ();
this.SourceLinkUri = sourceLinkUri;
this.assembly = assembly;
this.id = id;
@ -531,7 +536,8 @@ namespace WebAssembly.Net.Debugging {
internal void AddMethod (MethodInfo mi)
{
this.methods.Add (mi);
if (!this.methods.ContainsKey (mi.Token))
this.methods [mi.Token] = mi;
}
public string DebuggerFileName { get; }
@ -543,25 +549,93 @@ namespace WebAssembly.Net.Debugging {
public Uri SourceLinkUri { get; }
public Uri SourceUri { get; }
public IEnumerable<MethodInfo> Methods => this.methods;
public byte[] EmbeddedSource => doc.EmbeddedSource;
public IEnumerable<MethodInfo> Methods => this.methods.Values;
public string DocUrl => doc.Url;
public (int startLine, int startColumn, int endLine, int endColumn) GetExtents ()
{
var start = methods.OrderBy (m => m.StartLocation.Line).ThenBy (m => m.StartLocation.Column).First ();
var end = methods.OrderByDescending (m => m.EndLocation.Line).ThenByDescending (m => m.EndLocation.Column).First ();
var start = Methods.OrderBy (m => m.StartLocation.Line).ThenBy (m => m.StartLocation.Column).First ();
var end = Methods.OrderByDescending (m => m.EndLocation.Line).ThenByDescending (m => m.EndLocation.Column).First ();
return (start.StartLocation.Line, start.StartLocation.Column, end.EndLocation.Line, end.EndLocation.Column);
}
public async Task<byte[]> LoadSource ()
async Task<MemoryStream> GetDataAsync (Uri uri, CancellationToken token)
{
if (EmbeddedSource.Length > 0)
return await Task.FromResult (EmbeddedSource);
var mem = new MemoryStream ();
try {
if (uri.IsFile && File.Exists (uri.LocalPath)) {
using (var file = File.Open (SourceUri.LocalPath, FileMode.Open)) {
await file.CopyToAsync (mem, token);
mem.Position = 0;
}
} else if (uri.Scheme == "http" || uri.Scheme == "https") {
var client = new HttpClient ();
using (var stream = await client.GetStreamAsync (uri)) {
await stream.CopyToAsync (mem, token);
mem.Position = 0;
}
}
} catch (Exception) {
return null;
}
return mem;
}
static HashAlgorithm GetHashAlgorithm (DocumentHashAlgorithm algorithm)
{
switch (algorithm) {
case DocumentHashAlgorithm.SHA1: return SHA1.Create ();
case DocumentHashAlgorithm.SHA256: return SHA256.Create ();
case DocumentHashAlgorithm.MD5: return MD5.Create ();
}
return null;
}
bool CheckPdbHash (byte [] computedHash)
{
if (computedHash.Length != doc.Hash.Length)
return false;
for (var i = 0; i < computedHash.Length; i++)
if (computedHash[i] != doc.Hash[i])
return false;
return true;
}
byte[] ComputePdbHash (Stream sourceStream)
{
var algorithm = GetHashAlgorithm (doc.HashAlgorithm);
if (algorithm != null)
using (algorithm)
return algorithm.ComputeHash (sourceStream);
return Array.Empty<byte> ();
}
public async Task<Stream> GetSourceAsync (bool checkHash, CancellationToken token = default(CancellationToken))
{
if (doc.EmbeddedSource.Length > 0)
return new MemoryStream (doc.EmbeddedSource, false);
MemoryStream mem;
mem = await GetDataAsync (SourceUri, token);
if (mem != null && (!checkHash || CheckPdbHash (ComputePdbHash (mem)))) {
mem.Position = 0;
return mem;
}
mem = await GetDataAsync (SourceLinkUri, token);
if (mem != null && (!checkHash || CheckPdbHash (ComputePdbHash (mem)))) {
mem.Position = 0;
return mem;
}
return MemoryStream.Null;
}
public object ToScriptSource (int executionContextId, object executionContextAuxData)
{
return new {
@ -569,7 +643,7 @@ namespace WebAssembly.Net.Debugging {
url = Url,
executionContextId,
executionContextAuxData,
//hash = "abcdee" + id,
//hash: should be the v8 hash algo, managed implementation is pending
dotNetUrl = DotNetUrl,
};
}
@ -577,13 +651,18 @@ namespace WebAssembly.Net.Debugging {
internal class DebugStore {
List<AssemblyInfo> assemblies = new List<AssemblyInfo> ();
HttpClient client = new HttpClient ();
readonly HttpClient client;
readonly ILogger logger;
public DebugStore (ILogger logger) {
public DebugStore (ILogger logger, HttpClient client) {
this.client = client;
this.logger = logger;
}
public DebugStore (ILogger logger) : this (logger, new HttpClient ())
{
}
class DebugItem {
public string Url { get; set; }
public Task<byte[][]> Data { get; set; }
@ -623,7 +702,7 @@ namespace WebAssembly.Net.Debugging {
var bytes = await step.Data;
assembly = new AssemblyInfo (step.Url, bytes [0], bytes [1]);
} catch (Exception e) {
logger.LogDebug ($"Failed to Load {step.Url} ({e.Message})");
logger.LogDebug ($"Failed to load {step.Url} ({e.Message})");
}
if (assembly == null)
continue;
@ -686,7 +765,7 @@ namespace WebAssembly.Net.Debugging {
}
foreach (var method in doc.Methods) {
foreach (var sequencePoint in method.methodDef.DebugInformation.SequencePoints) {
foreach (var sequencePoint in method.DebugInformation.SequencePoints) {
if (!sequencePoint.IsHidden && Match (sequencePoint, start, end))
res.Add (new SourceLocation (method, sequencePoint));
}
@ -729,8 +808,7 @@ namespace WebAssembly.Net.Debugging {
yield break;
foreach (var method in sourceFile.Methods) {
foreach (var sequencePoint in method.methodDef.DebugInformation.SequencePoints) {
//FIXME handle multi doc methods
foreach (var sequencePoint in method.DebugInformation.SequencePoints) {
if (!sequencePoint.IsHidden && Match (sequencePoint, request.Line, request.Column))
yield return new SourceLocation (method, sequencePoint);
}

View File

@ -89,6 +89,9 @@ namespace WebAssembly.Net.Debugging {
public static Result Err (JObject err)
=> new Result (null, err);
public static Result Err (string msg)
=> new Result (null, JObject.FromObject (new { message = msg }));
public static Result Exception (Exception e)
=> new Result (null, JObject.FromObject (new { message = e.Message }));
@ -151,7 +154,7 @@ namespace WebAssembly.Net.Debugging {
public static MonoCommands GetScopeVariables (int scopeId, params int[] vars)
=> new MonoCommands ($"MONO.mono_wasm_get_variables({scopeId}, [ {string.Join (",", vars)} ])");
public static MonoCommands SetBreakpoint (string assemblyName, int methodToken, int ilOffset)
public static MonoCommands SetBreakpoint (string assemblyName, uint methodToken, int ilOffset)
=> new MonoCommands ($"MONO.mono_wasm_set_breakpoint (\"{assemblyName}\", {methodToken}, {ilOffset})");
public static MonoCommands RemoveBreakpoint (int breakpointId)
@ -251,6 +254,5 @@ namespace WebAssembly.Net.Debugging {
}
public int NextValueTypeId () => Interlocked.Increment (ref nextValueTypeId);
}
}

View File

@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Net.WebSockets;
@ -118,7 +119,10 @@ namespace WebAssembly.Net.Debugging {
void Send (WebSocket to, JObject o, CancellationToken token)
{
var sender = browser == to ? "Send-browser" : "Send-ide";
Log ("protocol", $"{sender}: {o}");
var method = o ["method"]?.ToString ();
//if (method != "Debugger.scriptParsed" && method != "Runtime.consoleAPICalled")
Log ("protocol", $"{sender}: " + JsonConvert.SerializeObject (o));
var bytes = Encoding.UTF8.GetBytes (o.ToString ());
var queue = GetQueueForSocket (to);
@ -165,9 +169,12 @@ namespace WebAssembly.Net.Debugging {
void ProcessBrowserMessage (string msg, CancellationToken token)
{
Log ("protocol", $"browser: {msg}");
var res = JObject.Parse (msg);
var method = res ["method"]?.ToString ();
//if (method != "Debugger.scriptParsed" && method != "Runtime.consoleAPICalled")
Log ("protocol", $"browser: {msg}");
if (res ["id"] == null)
pending_ops.Add (OnEvent (new SessionId (res ["sessionId"]?.Value<string> ()), res ["method"].Value<string> (), res ["params"] as JObject, token));
else

View File

@ -99,6 +99,8 @@ namespace WebAssembly.Net.Debugging {
return res.Value? ["result"]? ["value"]?.Value<bool> () ?? false;
}
static int bpIdGenerator;
protected override async Task<bool> AcceptCommand (MessageId id, string method, JObject args, CancellationToken token)
{
if (!contexts.TryGetValue (id, out var context))
@ -174,7 +176,8 @@ namespace WebAssembly.Net.Debugging {
}
case "Debugger.removeBreakpoint": {
return await RemoveBreakpoint (id, args, token);
await RemoveBreakpoint (id, args, token);
break;
}
case "Debugger.resume": {
@ -247,6 +250,68 @@ namespace WebAssembly.Net.Debugging {
}
break;
}
// Protocol extensions
case "Dotnet-test.setBreakpointByMethod": {
Console.WriteLine ("set-breakpoint-by-method: " + id + " " + args);
var store = await RuntimeReady (id, token);
string aname = args ["assemblyName"]?.Value<string> ();
string typeName = args ["typeName"]?.Value<string> ();
string methodName = args ["methodName"]?.Value<string> ();
if (aname == null || typeName == null || methodName == null) {
SendResponse (id, Result.Err ("Invalid protocol message '" + args + "'."), token);
return true;
}
// GetAssemblyByName seems to work on file names
var assembly = store.GetAssemblyByName (aname);
if (assembly == null)
assembly = store.GetAssemblyByName (aname + ".exe");
if (assembly == null)
assembly = store.GetAssemblyByName (aname + ".dll");
if (assembly == null) {
SendResponse (id, Result.Err ("Assembly '" + aname + "' not found."), token);
return true;
}
var type = assembly.GetTypeByName (typeName);
if (type == null) {
SendResponse (id, Result.Err ($"Type '{typeName}' not found."), token);
return true;
}
var methodInfo = type.Methods.FirstOrDefault (m => m.Name == methodName);
if (methodInfo == null) {
SendResponse (id, Result.Err ($"Method '{typeName}:{methodName}' not found."), token);
return true;
}
bpIdGenerator ++;
string bpid = "by-method-" + bpIdGenerator.ToString ();
var request = new BreakpointRequest (bpid, methodInfo);
context.BreakpointRequests[bpid] = request;
var loc = methodInfo.StartLocation;
var bp = await SetMonoBreakpoint (id, bpid, loc, token);
if (bp.State != BreakpointState.Active) {
// FIXME:
throw new NotImplementedException ();
}
var resolvedLocation = new {
breakpointId = bpid,
location = loc.AsLocation ()
};
SendEvent (id, "Debugger.breakpointResolved", JObject.FromObject (resolvedLocation), token);
SendResponse (id, Result.OkFromObject (new {
result = new { breakpointId = bpid, locations = new object [] { loc.AsLocation () }}
}), token);
return true;
}
}
return false;
@ -318,7 +383,7 @@ namespace WebAssembly.Net.Debugging {
foreach (var mono_frame in the_mono_frames) {
++frame_id;
var il_pos = mono_frame ["il_pos"].Value<int> ();
var method_token = mono_frame ["method_token"].Value<int> ();
var method_token = mono_frame ["method_token"].Value<uint> ();
var assembly_name = mono_frame ["assembly_name"].Value<string> ();
var store = await LoadStore (sessionId, token);
@ -672,9 +737,9 @@ namespace WebAssembly.Net.Debugging {
}
}
async Task<Breakpoint> SetMonoBreakpoint (SessionId sessionId, BreakpointRequest req, SourceLocation location, CancellationToken token)
async Task<Breakpoint> SetMonoBreakpoint (SessionId sessionId, string reqId, SourceLocation location, CancellationToken token)
{
var bp = new Breakpoint (req.Id, location, BreakpointState.Pending);
var bp = new Breakpoint (reqId, 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;
@ -742,12 +807,12 @@ namespace WebAssembly.Net.Debugging {
return store;
}
async Task<bool> RemoveBreakpoint(MessageId msg_id, JObject args, CancellationToken token) {
async Task RemoveBreakpoint(MessageId msg_id, JObject args, CancellationToken token) {
var bpid = args? ["breakpointId"]?.Value<string> ();
var context = GetContext (msg_id);
if (!context.BreakpointRequests.TryGetValue (bpid, out var breakpointRequest))
return false;
return;
foreach (var bp in breakpointRequest.Locations) {
var res = await SendMonoCommand (msg_id, MonoCommands.RemoveBreakpoint (bp.RemoteId), token);
@ -759,7 +824,6 @@ namespace WebAssembly.Net.Debugging {
}
}
breakpointRequest.Locations.Clear ();
return false;
}
async Task SetBreakpoint (SessionId sessionId, DebugStore store, BreakpointRequest req, CancellationToken token)
@ -774,8 +838,14 @@ namespace WebAssembly.Net.Debugging {
logger.LogDebug ("BP request for '{req}' runtime ready {context.RuntimeReady}", req, GetContext (sessionId).IsRuntimeReady);
var breakpoints = new List<Breakpoint> ();
// if column is specified the frontend wants the exact matches
// and will clear the bp if it isn't close enough
if (req.Column != 0)
locations = locations.Where (l => l.Column == req.Column).ToList ();
foreach (var loc in locations) {
var bp = await SetMonoBreakpoint (sessionId, req, loc, token);
var bp = await SetMonoBreakpoint (sessionId, req.Id, loc, token);
// If we didn't successfully enable the breakpoint
// don't add it to the list of locations for this id
@ -820,31 +890,18 @@ namespace WebAssembly.Net.Debugging {
return false;
var src_file = (await LoadStore (msg_id, token)).GetFileById (id);
var res = new StringWriter ();
try {
var uri = new Uri (src_file.Url);
string source = $"// Unable to find document {src_file.SourceUri}";
if (uri.IsFile && File.Exists(uri.LocalPath)) {
using (var f = new StreamReader (File.Open (uri.LocalPath, FileMode.Open))) {
await res.WriteAsync (await f.ReadToEndAsync ());
}
using (var data = await src_file.GetSourceAsync (checkHash: false, token: token)) {
if (data.Length == 0)
return false;
source = res.ToString ();
} else if (src_file.SourceUri.IsFile && File.Exists(src_file.SourceUri.LocalPath)) {
using (var f = new StreamReader (File.Open (src_file.SourceUri.LocalPath, FileMode.Open))) {
await res.WriteAsync (await f.ReadToEndAsync ());
}
source = res.ToString ();
} else if(src_file.SourceLinkUri != null) {
var doc = await new WebClient ().DownloadStringTaskAsync (src_file.SourceLinkUri);
await res.WriteAsync (doc);
source = res.ToString ();
using (var reader = new StreamReader (data))
source = await reader.ReadToEndAsync ();
}
SendResponse (msg_id, Result.OkFromObject (new { scriptSource = source }), token);
} catch (Exception e) {
var o = new {