Update Mono WebAssembly for Blazor (https://github.com/aspnet/Blazor/pull/1807)

This commit is contained in:
Steve Sanderson 2019-05-23 13:10:53 +01:00 committed by GitHub
parent dbe9ab7dd5
commit 6c5e1690ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 330 additions and 123 deletions

View File

@ -27,6 +27,11 @@
$(RestoreSources);
https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json;
</RestoreSources>
<!-- TODO remove this once we move Microsoft.AspNetCore.Blazor.Mono to a non-myget feed -->
<RestoreSources>
$(RestoreSources);
https://dotnet.myget.org/F/blazor-dev/api/v3/index.json;
</RestoreSources>
<!-- In an orchestrated build, this may be overriden to other Azure feeds. -->
<DotNetAssetRootUrl Condition="'$(DotNetAssetRootUrl)'==''">https://dotnetcli.blob.core.windows.net/dotnet/</DotNetAssetRootUrl>

View File

@ -165,7 +165,7 @@
<MicrosoftWebXdtPackageVersion>1.4.0</MicrosoftWebXdtPackageVersion>
<SystemIdentityModelTokensJwtPackageVersion>5.3.0</SystemIdentityModelTokensJwtPackageVersion>
<!-- Dependencies for Blazor. -->
<MicrosoftAspNetCoreBlazorMonoPackageVersion>0.10.0-preview-20190325.1</MicrosoftAspNetCoreBlazorMonoPackageVersion>
<MicrosoftAspNetCoreBlazorMonoPackageVersion>0.10.0-preview-20190523.1</MicrosoftAspNetCoreBlazorMonoPackageVersion>
<!-- Packages from 2.1/2.2 branches used for site extension build -->
<MicrosoftAspNetCoreAzureAppServicesSiteExtension21PackageVersion>2.1.1</MicrosoftAspNetCoreAzureAppServicesSiteExtension21PackageVersion>
<MicrosoftAspNetCoreAzureAppServicesSiteExtension22PackageVersion>2.2.0</MicrosoftAspNetCoreAzureAppServicesSiteExtension22PackageVersion>

View File

@ -6,7 +6,7 @@
<PropertyGroup Label="Blazor build outputs">
<MonoLinkerI18NAssemblies>none</MonoLinkerI18NAssemblies> <!-- See Mono linker docs - allows comma-separated values from: none,all,cjk,mideast,other,rare,west -->
<AdditionalMonoLinkerOptions>--verbose --strip-security true --exclude-feature com --exclude-feature sre -v false -c link -u link -b true</AdditionalMonoLinkerOptions>
<AdditionalMonoLinkerOptions>--disable-opt unreachablebodies --verbose --strip-security true --exclude-feature com --exclude-feature sre -v false -c link -u link -b true</AdditionalMonoLinkerOptions>
<BaseBlazorDistPath>dist/</BaseBlazorDistPath>
<BaseBlazorPackageContentOutputPath>$(BaseBlazorDistPath)_content/</BaseBlazorPackageContentOutputPath>
<BaseBlazorRuntimeOutputPath>$(BaseBlazorDistPath)_framework/</BaseBlazorRuntimeOutputPath>

View File

@ -1,20 +0,0 @@
@echo off
echo |----
echo | Copying the ws-proxy sources here is a temporary step until ws-proxy is
echo | distributed as a NuGet package.
echo | ...
echo | Instead of dealing with Git submodules, this script simply fetches the
echo | latest sources so they can be built directly inside this project (hence
echo | we don't have to publish our own separate package for this).
echo | ...
echo | When updating, you'll need to re-apply any patches we've made manually.
echo |----
@echo on
cd /D "%~dp0"
rmdir /s /q ws-proxy
git clone https://github.com/kumpera/ws-proxy.git
rmdir /s /q ws-proxy\.git
del ws-proxy\*.csproj
del ws-proxy\*.sln
del ws-proxy\Program.cs

View File

@ -6,6 +6,9 @@ using Mono.Cecil.Cil;
using System.Linq;
using Newtonsoft.Json.Linq;
using System.Net.Http;
using Mono.Cecil.Pdb;
using Newtonsoft.Json;
using System.Text.RegularExpressions;
namespace WsProxy {
internal class BreakPointRequest {
@ -18,17 +21,29 @@ namespace WsProxy {
return $"BreakPointRequest Assembly: {Assembly} File: {File} Line: {Line} Column: {Column}";
}
public static BreakPointRequest Parse (JObject args)
public static BreakPointRequest Parse (JObject args, DebugStore store)
{
if (args == null)
return null;
var url = args? ["url"]?.Value<string> ();
if (!url.StartsWith ("dotnet://", StringComparison.InvariantCulture))
if (url == null) {
var urlRegex = args?["urlRegex"].Value<string>();
var sourceFile = store.GetFileByUrlRegex (urlRegex);
url = sourceFile?.DotNetUrl;
}
if (url != null && !url.StartsWith ("dotnet://", StringComparison.InvariantCulture)) {
var sourceFile = store.GetFileByUrl (url);
url = sourceFile?.DotNetUrl;
}
if (url == null)
return null;
var parts = url.Substring ("dotnet://".Length).Split ('/');
if (parts.Length != 2)
var parts = ParseDocumentUrl (url);
if (parts.Assembly == null)
return null;
var line = args? ["lineNumber"]?.Value<int> ();
@ -37,12 +52,24 @@ namespace WsProxy {
return null;
return new BreakPointRequest () {
Assembly = parts [0],
File = parts [1],
Assembly = parts.Assembly,
File = parts.DocumentPath,
Line = line.Value,
Column = column.Value
};
}
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);
}
}
}
@ -101,7 +128,7 @@ namespace WsProxy {
public SourceLocation (MethodInfo mi, SequencePoint sp)
{
this.id = mi.SourceId;
this.line = sp.StartLine;
this.line = sp.StartLine - 1;
this.column = sp.StartColumn - 1;
this.cliLoc = new CliLocation (mi, sp.Offset);
}
@ -260,13 +287,13 @@ namespace WsProxy {
}
}
internal class AssemblyInfo {
static int next_id;
ModuleDefinition image;
readonly int id;
Dictionary<int, MethodInfo> methods = new Dictionary<int, MethodInfo> ();
readonly List<SourceFile> sources = new List<SourceFile> ();
Dictionary<string, string> sourceLinkMappings = new Dictionary<string, string>();
readonly List<SourceFile> sources = new List<SourceFile>();
public AssemblyInfo (byte[] assembly, byte[] pdb)
{
@ -274,16 +301,35 @@ namespace WsProxy {
this.id = ++next_id;
}
ReaderParameters rp = new ReaderParameters (/*ReadingMode.Immediate*/);
if (pdb != null) {
rp.ReadSymbols = true;
rp.SymbolReaderProvider = new PortablePdbReaderProvider ();
rp.SymbolStream = new MemoryStream (pdb);
try {
ReaderParameters rp = new ReaderParameters (/*ReadingMode.Immediate*/);
if (pdb != null) {
rp.ReadSymbols = true;
rp.SymbolReaderProvider = new PortablePdbReaderProvider ();
rp.SymbolStream = new MemoryStream (pdb);
}
rp.ReadingMode = ReadingMode.Immediate;
rp.InMemory = true;
this.image = ModuleDefinition.ReadModule (new MemoryStream (assembly), rp);
} catch (BadImageFormatException ex) {
Console.WriteLine ($"Failed to read assembly as portable PDB: {ex.Message}");
}
rp.InMemory = true;
if (this.image == null) {
ReaderParameters rp = new ReaderParameters (/*ReadingMode.Immediate*/);
if (pdb != null) {
rp.ReadSymbols = true;
rp.SymbolReaderProvider = new NativePdbReaderProvider ();
rp.SymbolStream = new MemoryStream (pdb);
}
this.image = ModuleDefinition.ReadModule (new MemoryStream (assembly), rp);
rp.ReadingMode = ReadingMode.Immediate;
rp.InMemory = true;
this.image = ModuleDefinition.ReadModule (new MemoryStream (assembly), rp);
}
Populate ();
}
@ -294,6 +340,8 @@ namespace WsProxy {
void Populate ()
{
ProcessSourceLink();
var d2s = new Dictionary<Document, SourceFile> ();
Func<Document, SourceFile> get_src = (doc) => {
@ -301,33 +349,88 @@ namespace WsProxy {
return null;
if (d2s.ContainsKey (doc))
return d2s [doc];
var src = new SourceFile (this, sources.Count, doc);
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)) {
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) {
if (first_doc == null && !sp.Document.Url.EndsWith (".g.cs")) {
first_doc = sp.Document;
} 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}");
}
// 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}");
//}
}
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);
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);
}
}
}
private void ProcessSourceLink ()
{
var sourceLinkDebugInfo = image.CustomDebugInformations.FirstOrDefault (i => i.Kind == CustomDebugInformationKind.SourceLink);
if (sourceLinkDebugInfo != null) {
var sourceLinkContent = ((SourceLinkDebugInformation)sourceLinkDebugInfo).Content;
if (sourceLinkContent != null) {
var jObject = JObject.Parse (sourceLinkContent) ["documents"];
sourceLinkMappings = JsonConvert.DeserializeObject<Dictionary<string, string>> (jObject.ToString ());
}
}
}
private Uri GetSourceLinkUrl (string document)
{
if (sourceLinkMappings.TryGetValue (document, out string url)) {
return new Uri (url);
}
foreach (var sourceLinkDocument in sourceLinkMappings) {
string key = sourceLinkDocument.Key;
if (Path.GetFileName (key) != "*") {
continue;
}
var keyTrim = key.TrimEnd ('*');
if (document.StartsWith(keyTrim, StringComparison.OrdinalIgnoreCase)) {
var docUrlPart = document.Replace (keyTrim, "");
return new Uri (sourceLinkDocument.Value.TrimEnd ('*') + docUrlPart);
}
}
return null;
}
private 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);
if (rel.Contains (Path.DirectorySeparatorChar.ToString ()) == false) {
rel = $".{ Path.DirectorySeparatorChar }{ rel }";
}
return rel;
}
public IEnumerable<SourceFile> Sources {
get { return this.sources; }
}
@ -342,9 +445,9 @@ namespace WsProxy {
public MethodInfo GetMethodByToken (int token)
{
return methods [token];
methods.TryGetValue (token, out var value);
return value;
}
}
internal class SourceFile {
@ -353,23 +456,36 @@ namespace WsProxy {
int id;
Document doc;
internal SourceFile (AssemblyInfo assembly, int id, Document doc)
internal SourceFile (AssemblyInfo assembly, int id, Document doc, Uri sourceLinkUri)
{
this.methods = new HashSet<MethodInfo> ();
this.SourceLinkUri = sourceLinkUri;
this.assembly = assembly;
this.id = id;
this.doc = doc;
this.DebuggerFileName = doc.Url.Replace ("\\", "/").Replace (":", "");
this.SourceUri = new Uri ((Path.IsPathRooted (doc.Url) ? "file://" : "") + doc.Url, UriKind.RelativeOrAbsolute);
if (SourceUri.IsFile && File.Exists (SourceUri.LocalPath)) {
this.Url = this.SourceUri.ToString ();
} else {
this.Url = DotNetUrl;
}
}
internal void AddMethod (MethodInfo mi)
{
this.methods.Add (mi);
}
public string FileName => Path.GetFileName (doc.Url);
public string Url => $"dotnet://{assembly.Name}/{FileName}";
public string DebuggerFileName { get; }
public string Url { get; }
public string AssemblyName => assembly.Name;
public string DotNetUrl => $"dotnet://{assembly.Name}/{DebuggerFileName}";
public string DocHashCode => "abcdee" + id;
public SourceId SourceId => new SourceId (assembly.Id, this.id);
public string LocalPath => doc.Url;
public Uri SourceLinkUri { get; }
public Uri SourceUri { get; }
public IEnumerable<MethodInfo> Methods => this.methods;
}
@ -377,17 +493,18 @@ namespace WsProxy {
internal class DebugStore {
List<AssemblyInfo> assemblies = new List<AssemblyInfo> ();
public DebugStore (string[] loaded_files)
public DebugStore (string [] loaded_files)
{
bool MatchPdb (string asm, string pdb) {
bool MatchPdb (string asm, string pdb)
{
return Path.ChangeExtension (asm, "pdb") == pdb;
}
var asm_files = new List<string> ();
var pdb_files = new List<string> ();
foreach (var f in loaded_files) {
var file_name = f.ToLower ();
if (file_name.EndsWith (".pdb", StringComparison.Ordinal))
var file_name = f;
if (file_name.EndsWith (".pdb", StringComparison.OrdinalIgnoreCase))
pdb_files.Add (file_name);
else
asm_files.Add (file_name);
@ -395,14 +512,18 @@ namespace WsProxy {
//FIXME make this parallel
foreach (var p in asm_files) {
var pdb = pdb_files.FirstOrDefault (n => MatchPdb (p, n));
HttpClient h = new HttpClient ();
var assembly_bytes = h.GetByteArrayAsync (p).Result;
byte[] pdb_bytes = null;
if (pdb != null)
pdb_bytes = h.GetByteArrayAsync (pdb).Result;
try {
var pdb = pdb_files.FirstOrDefault (n => MatchPdb (p, n));
HttpClient h = new HttpClient ();
var assembly_bytes = h.GetByteArrayAsync (p).Result;
byte [] pdb_bytes = null;
if (pdb != null)
pdb_bytes = h.GetByteArrayAsync (pdb).Result;
this.assemblies.Add (new AssemblyInfo (assembly_bytes, pdb_bytes));
this.assemblies.Add (new AssemblyInfo (assembly_bytes, pdb_bytes));
} catch (Exception e) {
Console.WriteLine ($"Failed to read {p} ({e.Message})");
}
}
}
@ -412,7 +533,6 @@ namespace WsProxy {
foreach (var s in a.Sources)
yield return s;
}
}
public SourceFile GetFileById (SourceId id)
@ -426,26 +546,23 @@ namespace WsProxy {
}
/*
Matching logic here is hilarious and it goes like this:
We inject one line at the top of all sources to make it easy to identify them [1].
V8 uses zero based indexing for both line and column.
PPDBs uses one based indexing for both line and column.
Which means that:
- for lines, values are already adjusted (v8 numbers come +1 due to the injected line)
- for columns, we need to +1 the v8 numbers
[1] It's so we can deal with the Runtime.compileScript ide cmd
*/
static bool Match (SequencePoint sp, SourceLocation start, SourceLocation end)
{
if (start.Line > sp.StartLine)
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 + 1) > sp.StartColumn && start.Line == sp.StartLine)
if (start.Column > spStart.Column && start.Line == sp.StartLine)
return false;
if (end.Line < sp.EndLine)
if (end.Line < spEnd.Line)
return false;
if ((end.Column + 1) < sp.EndColumn && end.Line == sp.EndLine)
if (end.Column < spEnd.Column && end.Line == spEnd.Line)
return false;
return true;
@ -477,28 +594,24 @@ namespace WsProxy {
}
/*
Matching logic here is hilarious and it goes like this:
We inject one line at the top of all sources to make it easy to identify them [1].
V8 uses zero based indexing for both line and column.
PPDBs uses one based indexing for both line and column.
Which means that:
- for lines, values are already adjusted (v8 numbers come + 1 due to the injected line)
- for columns, we need to +1 the v8 numbers
[1] It's so we can deal with the Runtime.compileScript ide cmd
*/
static bool Match (SequencePoint sp, int line, int column)
{
if (sp.StartLine > line || sp.EndLine < line)
var bp = (line: line + 1, column: column + 1);
if (sp.StartLine > bp.line || sp.EndLine < bp.line)
return false;
//Chrome sends a zero column even if getPossibleBreakpoints say something else
if (column == 0)
return true;
if (sp.StartColumn > (column + 1) && sp.StartLine == line)
if (sp.StartColumn > bp.column && sp.StartLine == bp.line)
return false;
if (sp.EndColumn < (column + 1) && sp.EndLine == line)
if (sp.EndColumn < bp.column && sp.EndLine == bp.line)
return false;
return true;
@ -506,8 +619,11 @@ namespace WsProxy {
public SourceLocation FindBestBreakpoint (BreakPointRequest req)
{
var asm = this.assemblies.FirstOrDefault (a => a.Name == req.Assembly);
var src = asm.Sources.FirstOrDefault (s => s.FileName == req.File);
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));
if (src == null)
return null;
foreach (var m in src.Methods) {
foreach (var sp in m.methodDef.DebugInformation.SequencePoints) {
@ -521,8 +637,15 @@ namespace WsProxy {
}
public string ToUrl (SourceLocation location)
=> location != null ? GetFileById (location.Id).Url : "";
public SourceFile GetFileByUrlRegex (string urlRegex)
{
return GetFileById (location.Id).Url;
var regex = new Regex (urlRegex);
return AllSources ().FirstOrDefault (file => regex.IsMatch (file.Url.ToString()));
}
public SourceFile GetFileByUrl (string url)
=> AllSources ().FirstOrDefault (file => file.Url.ToString() == url);
}
}

View File

@ -8,6 +8,7 @@ using System.Threading;
using System.IO;
using System.Text;
using System.Collections.Generic;
using System.Net;
namespace WsProxy {
@ -20,6 +21,8 @@ namespace WsProxy {
public const string REMOVE_BREAK_POINT = "MONO.mono_wasm_remove_breakpoint({0})";
public const string GET_LOADED_FILES = "MONO.mono_wasm_get_loaded_files()";
public const string CLEAR_ALL_BREAKPOINTS = "MONO.mono_wasm_clear_all_breakpoints()";
public const string GET_OBJECT_PROPERTIES = "MONO.mono_wasm_get_object_properties({0})";
public const string GET_ARRAY_VALUES = "MONO.mono_wasm_get_array_values({0})";
}
internal enum MonoErrorCodes {
@ -128,7 +131,7 @@ namespace WsProxy {
case "Debugger.getScriptSource": {
var script_id = args? ["scriptId"]?.Value<string> ();
if (script_id.StartsWith ("dotnet://", StringComparison.InvariantCultureIgnoreCase)) {
OnGetScriptSource (id, script_id, token);
await OnGetScriptSource (id, script_id, token);
return true;
}
@ -154,7 +157,7 @@ namespace WsProxy {
case "Debugger.setBreakpointByUrl": {
Info ($"BP req {args}");
var bp_req = BreakPointRequest.Parse (args);
var bp_req = BreakPointRequest.Parse (args, store);
if (bp_req != null) {
await SetBreakPoint (id, bp_req, token);
return true;
@ -200,7 +203,14 @@ namespace WsProxy {
await GetScopeProperties (id, int.Parse (objId.Substring ("dotnet:scope:".Length)), token);
return true;
}
if (objId.StartsWith("dotnet:", StringComparison.InvariantCulture))
{
if (objId.StartsWith("dotnet:object:", StringComparison.InvariantCulture))
await GetDetails(id, int.Parse(objId.Substring("dotnet:object:".Length)), token, MonoCommands.GET_OBJECT_PROPERTIES);
if (objId.StartsWith("dotnet:array:", StringComparison.InvariantCulture))
await GetDetails(id, int.Parse(objId.Substring("dotnet:array:".Length)), token, MonoCommands.GET_ARRAY_VALUES);
return true;
}
break;
}
}
@ -213,6 +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);
}
async Task OnBreakPointHit (JObject args, CancellationToken token)
@ -257,9 +268,9 @@ namespace WsProxy {
var src = bp == null ? null : store.GetFileById (bp.Location.Id);
var callFrames = new List<JObject> ();
foreach (var f in orig_callframes) {
var function_name = f ["functionName"]?.Value<string> ();
var url = f ["url"]?.Value<string> ();
foreach (var frame in orig_callframes) {
var function_name = frame ["functionName"]?.Value<string> ();
var url = frame ["url"]?.Value<string> ();
if ("mono_wasm_fire_bp" == function_name || "_mono_wasm_fire_bp" == function_name) {
var frames = new List<Frame> ();
int frame_id = 0;
@ -271,14 +282,19 @@ namespace WsProxy {
var asm = store.GetAssemblyByName (assembly_name);
var method = asm.GetMethodByToken (method_token);
var location = method.GetLocationByIl (il_pos);
if (method == null) {
Info ($"Unable to find il offset: {il_pos} in method token: {method_token} assembly name: {assembly_name}");
continue;
}
var location = method?.GetLocationByIl (il_pos);
// When hitting a breakpoint on the "IncrementCount" method in the standard
// Blazor project template, one of the stack frames is inside mscorlib.dll
// and we get location==null for it. It will trigger a NullReferenceException
// if we don't skip over that stack frame.
if (location == null)
{
if (location == null) {
continue;
}
@ -288,7 +304,7 @@ namespace WsProxy {
callFrames.Add (JObject.FromObject (new {
functionName = method.Name,
callFrameId = $"dotnet:scope:{frame_id}",
functionLocation = method.StartLocation.ToJObject (),
location = location.ToJObject (),
@ -300,25 +316,24 @@ namespace WsProxy {
type = "local",
@object = new {
@type = "object",
className = "Object",
className = "Object",
description = "Object",
objectId = $"dotnet:scope:{frame_id}"
objectId = $"dotnet:scope:{frame_id}",
},
name = method.Name,
startLocation = method.StartLocation.ToJObject (),
endLocation = method.EndLocation.ToJObject (),
}
},
@this = new {
}
}},
@this = new { }
}));
++frame_id;
this.current_callstack = frames;
}
} else if (!url.StartsWith ("wasm://wasm/", StringComparison.InvariantCulture)) {
callFrames.Add (f);
} else if (!(function_name.StartsWith ("wasm-function", StringComparison.InvariantCulture)
|| url.StartsWith ("wasm://wasm/", StringComparison.InvariantCulture))) {
callFrames.Add (frame);
}
}
@ -393,6 +408,57 @@ namespace WsProxy {
await SendCommand ("Debugger.resume", new JObject (), token);
}
async Task GetDetails(int msg_id, int object_id, CancellationToken token, string command)
{
var o = JObject.FromObject(new
{
expression = string.Format(command, object_id),
objectGroup = "mono_debugger",
includeCommandLineAPI = false,
silent = false,
returnByValue = true,
});
var res = await SendCommand("Runtime.evaluate", o, token);
//if we fail we just buble that to the IDE (and let it panic over it)
if (res.IsErr)
{
SendResponse(msg_id, res, token);
return;
}
var values = res.Value?["result"]?["value"]?.Values<JObject>().ToArray();
var var_list = new List<JObject>();
// 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 = values[i+1]["value"]
}));
}
o = JObject.FromObject(new
{
result = var_list
});
SendResponse(msg_id, Result.Ok(o), token);
}
async Task GetScopeProperties (int msg_id, int scope_id, CancellationToken token)
{
var scope = this.current_callstack.FirstOrDefault (s => s.Id == scope_id);
@ -425,6 +491,10 @@ namespace WsProxy {
// 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; values != null && i < vars.Length; ++i) {
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"]
@ -485,7 +555,8 @@ namespace WsProxy {
url = s.Url,
executionContextId = this.ctx_id,
hash = s.DocHashCode,
executionContextAuxData = this.aux_ctx_data
executionContextAuxData = this.aux_ctx_data,
dotNetUrl = s.DotNetUrl
});
//Debug ($"\tsending {s.Url}");
SendEvent ("Debugger.scriptParsed", ok, token);
@ -640,23 +711,51 @@ namespace WsProxy {
}
void OnGetScriptSource (int msg_id, string script_id, CancellationToken token)
async Task OnGetScriptSource (int msg_id, string script_id, CancellationToken token)
{
var id = new SourceId (script_id);
var src_file = store.GetFileById (id);
var res = new StringWriter ();
res.WriteLine ($"//dotnet:{id}");
//res.WriteLine ($"//{id}");
using (var f = new StreamReader (File.Open (src_file.LocalPath, FileMode.Open))) {
res.Write (f.ReadToEnd ());
try {
var uri = new Uri (src_file.Url);
if (uri.IsFile && File.Exists(uri.LocalPath)) {
using (var f = new StreamReader (File.Open (src_file.SourceUri.LocalPath, FileMode.Open))) {
await res.WriteAsync (await f.ReadToEndAsync ());
}
var o = JObject.FromObject (new {
scriptSource = res.ToString ()
});
SendResponse (msg_id, Result.Ok (o), token);
} else if(src_file.SourceLinkUri != null) {
var doc = await new WebClient ().DownloadStringTaskAsync (src_file.SourceLinkUri);
await res.WriteAsync (doc);
var o = JObject.FromObject (new {
scriptSource = res.ToString ()
});
SendResponse (msg_id, Result.Ok (o), token);
} else {
var o = JObject.FromObject (new {
scriptSource = $"// Unable to find document {src_file.SourceUri}"
});
SendResponse (msg_id, Result.Ok (o), token);
}
} catch (Exception e) {
var o = JObject.FromObject (new {
scriptSource = $"// Unable to read document ({e.Message})\n" +
$"Local path: {src_file?.SourceUri}\n" +
$"SourceLink path: {src_file?.SourceLinkUri}\n"
});
SendResponse (msg_id, Result.Ok (o), token);
}
var o = JObject.FromObject (new {
scriptSource = res.ToString ()
});
SendResponse (msg_id, Result.Ok (o), token);
}
}
}

View File

@ -144,7 +144,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);
@ -256,7 +256,7 @@ namespace WsProxy {
}
// , HttpContext context)
public async Task Run (Uri browserUri, WebSocket ideSocket)
public async Task Run (Uri browserUri, WebSocket ideSocket)
{
Debug ("wsproxy start");
using (this.ide = ideSocket) {
@ -276,7 +276,7 @@ namespace WsProxy {
try {
while (!x.IsCancellationRequested) {
var task = await Task.WhenAny (pending_ops);
var task = await Task.WhenAny (pending_ops.ToArray ());
//Console.WriteLine ("pump {0} {1}", task, pending_ops.IndexOf (task));
if (task == pending_ops [0]) {
var msg = ((Task<string>)task).Result;