Merge branch 'blazor-wasm' into prkrishn/purge

This commit is contained in:
Pranav K 2020-02-05 12:29:19 -08:00 committed by GitHub
commit 9450e95f2e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 582 additions and 289 deletions

View File

@ -57,235 +57,249 @@
<Uri>https://github.com/dotnet/efcore</Uri> <Uri>https://github.com/dotnet/efcore</Uri>
<Sha>7ae89d6adb7433b6ae9c37091e57a1226092ef46</Sha> <Sha>7ae89d6adb7433b6ae9c37091e57a1226092ef46</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.AspNetCore.Analyzer.Testing" Version="3.1.2-servicing.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.AspNetCore.Analyzer.Testing" Version="3.1.2-servicing.20067.6" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.AspNetCore.BenchmarkRunner.Sources" Version="3.1.2-servicing.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.AspNetCore.BenchmarkRunner.Sources" Version="3.1.2-servicing.20067.6" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.ActivatorUtilities.Sources" Version="3.1.2-servicing.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.ActivatorUtilities.Sources" Version="3.1.2-servicing.20067.6" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Caching.Abstractions" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.Caching.Abstractions" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Caching.Memory" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.Caching.Memory" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Caching.SqlServer" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.Caching.SqlServer" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Caching.StackExchangeRedis" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.Caching.StackExchangeRedis" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
<Dependency Name="Microsoft.Extensions.Caching.Abstractions" Version="3.1.0" Pinned="true">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>1c5c7777ea9a19d54ab67ec1521665c99460efc5</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Caching.Memory" Version="3.1.0" Pinned="true">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>1c5c7777ea9a19d54ab67ec1521665c99460efc5</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Caching.SqlServer" Version="3.1.0" Pinned="true">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>1c5c7777ea9a19d54ab67ec1521665c99460efc5</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Caching.StackExchangeRedis" Version="3.1.0" Pinned="true">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>1c5c7777ea9a19d54ab67ec1521665c99460efc5</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.CommandLineUtils.Sources" Version="3.1.2-servicing.20067.6" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.CommandLineUtils.Sources" Version="3.1.2-servicing.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.Configuration.AzureKeyVault" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.AzureKeyVault" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.Configuration.Binder" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.Binder" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.Configuration.CommandLine" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.CommandLine" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.Configuration.FileExtensions" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.FileExtensions" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.Configuration.Ini" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.Ini" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.Configuration.Json" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.Json" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.Configuration.KeyPerFile" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.KeyPerFile" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.Configuration.UserSecrets" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.UserSecrets" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.Configuration.Xml" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.Xml" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.Configuration" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Configuration" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.DependencyInjection" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.DependencyInjection" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.DiagnosticAdapter" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.DiagnosticAdapter" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.Diagnostics.HealthChecks" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Diagnostics.HealthChecks" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.FileProviders.Abstractions" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.FileProviders.Abstractions" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.FileProviders.Composite" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.FileProviders.Composite" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.FileProviders.Embedded" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.FileProviders.Embedded" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.FileProviders.Physical" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.FileProviders.Physical" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.FileSystemGlobbing" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.FileSystemGlobbing" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.HashCodeCombiner.Sources" Version="3.1.2-servicing.20067.6" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.HashCodeCombiner.Sources" Version="3.1.2-servicing.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.Hosting" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Hosting" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.HostFactoryResolver.Sources" Version="3.1.2-servicing.20067.6" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.HostFactoryResolver.Sources" Version="3.1.2-servicing.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.Http" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Http" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.Localization.Abstractions" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Localization.Abstractions" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.Localization" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Localization" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.Logging.Abstractions" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Logging.Abstractions" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.Logging.AzureAppServices" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Logging.AzureAppServices" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.Logging.Configuration" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Logging.Configuration" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.Logging.Console" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Logging.Console" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.Logging.Debug" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Logging.Debug" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.Logging.EventSource" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Logging.EventSource" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.Logging.EventLog" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Logging.EventLog" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.Logging.TraceSource" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
<Dependency Name="Microsoft.Extensions.Logging.Testing" Version="3.1.2-servicing.20067.6" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Logging.TraceSource" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.Logging" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Logging.Testing" Version="3.1.2-servicing.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.ObjectPool" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Logging" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.Options.ConfigurationExtensions" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.ObjectPool" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.Options.DataAnnotations" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Options.ConfigurationExtensions" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.Options" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Options.DataAnnotations" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.ParameterDefaultValue.Sources" Version="3.1.2-servicing.20067.6" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Options" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.Primitives" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.ParameterDefaultValue.Sources" Version="3.1.2-servicing.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.TypeNameHelper.Sources" Version="3.1.2-servicing.20067.6" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.Primitives" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.ValueStopwatch.Sources" Version="3.1.2-servicing.20067.6" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.TypeNameHelper.Sources" Version="3.1.2-servicing.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.Extensions.WebEncoders" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.ValueStopwatch.Sources" Version="3.1.2-servicing.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.JSInterop" Version="3.1.2" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.WebEncoders" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Mono.WebAssembly.Interop" Version="3.1.2-preview4.20067.6" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency>
<Dependency Name="Microsoft.JSInterop" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency>
<Dependency Name="Mono.WebAssembly.Interop" Version="3.1.2-preview4.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
@ -379,7 +393,7 @@
</Dependency> </Dependency>
<Dependency Name="Microsoft.Extensions.DependencyModel" Version="3.1.2" CoherentParentDependency="Microsoft.Extensions.Logging"> <Dependency Name="Microsoft.Extensions.DependencyModel" Version="3.1.2" CoherentParentDependency="Microsoft.Extensions.Logging">
<Uri>https://github.com/dotnet/core-setup</Uri> <Uri>https://github.com/dotnet/core-setup</Uri>
<Sha>916b5cba268e1e1e803243004f4276cf40b2dda8</Sha> <Sha>65f04fb6db7a5e198d05dbebd5c4ad21eb018f89</Sha>
</Dependency> </Dependency>
<!-- <!--
Win-x64 is used here because we have picked an arbitrary runtime identifier to flow the version of the latest NETCore.App runtime. Win-x64 is used here because we have picked an arbitrary runtime identifier to flow the version of the latest NETCore.App runtime.
@ -413,7 +427,7 @@
<Uri>https://github.com/dotnet/corefx</Uri> <Uri>https://github.com/dotnet/corefx</Uri>
<Sha>0f7f38c4fd323b26da10cce95f857f77f0f09b48</Sha> <Sha>0f7f38c4fd323b26da10cce95f857f77f0f09b48</Sha>
</Dependency> </Dependency>
<Dependency Name="Internal.AspNetCore.Analyzers" Version="3.1.2-servicing.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Internal.AspNetCore.Analyzers" Version="3.1.2-servicing.20067.6" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>
@ -429,7 +443,7 @@
<Uri>https://github.com/dotnet/arcade</Uri> <Uri>https://github.com/dotnet/arcade</Uri>
<Sha>4d80b9cfa53e309c8f685abff3512f60c3d8a3d1</Sha> <Sha>4d80b9cfa53e309c8f685abff3512f60c3d8a3d1</Sha>
</Dependency> </Dependency>
<Dependency Name="Microsoft.AspNetCore.Testing" Version="3.1.2-servicing.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore"> <Dependency Name="Microsoft.AspNetCore.Testing" Version="3.1.2-servicing.20067.6" Pinned="true">
<Uri>https://github.com/dotnet/extensions</Uri> <Uri>https://github.com/dotnet/extensions</Uri>
<Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha>
</Dependency> </Dependency>

View File

@ -3,7 +3,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
@ -12,7 +11,9 @@ using System.Runtime.InteropServices;
using System.Text.Json; using System.Text.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using WsProxy; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using WebAssembly.Net.Debugging;
namespace Microsoft.AspNetCore.Builder namespace Microsoft.AspNetCore.Builder
{ {
@ -50,7 +51,8 @@ namespace Microsoft.AspNetCore.Builder
if (requestPath.Equals("/_framework/debug/ws-proxy", StringComparison.OrdinalIgnoreCase)) if (requestPath.Equals("/_framework/debug/ws-proxy", StringComparison.OrdinalIgnoreCase))
{ {
return DebugWebSocketProxyRequest(context); var loggerFactory = app.ApplicationServices.GetRequiredService<ILoggerFactory>();
return DebugWebSocketProxyRequest(loggerFactory, context);
} }
if (requestPath.Equals("/_framework/debug", StringComparison.OrdinalIgnoreCase)) if (requestPath.Equals("/_framework/debug", StringComparison.OrdinalIgnoreCase))
@ -111,7 +113,8 @@ namespace Microsoft.AspNetCore.Builder
var proxiedTabInfos = availableTabs.Select(tab => var proxiedTabInfos = availableTabs.Select(tab =>
{ {
var underlyingV8Endpoint = tab.WebSocketDebuggerUrl; var underlyingV8Endpoint = tab.WebSocketDebuggerUrl;
var proxiedV8Endpoint = $"ws://{request.Host}{request.PathBase}/_framework/debug/ws-proxy?browser={WebUtility.UrlEncode(underlyingV8Endpoint)}"; var proxiedScheme = request.IsHttps ? "wss" : "ws";
var proxiedV8Endpoint = $"{proxiedScheme}://{request.Host}{request.PathBase}/_framework/debug/ws-proxy?browser={WebUtility.UrlEncode(underlyingV8Endpoint)}";
return new return new
{ {
description = "", description = "",
@ -129,10 +132,42 @@ namespace Microsoft.AspNetCore.Builder
} }
else if (requestPath.Equals("/json/version", StringComparison.OrdinalIgnoreCase)) else if (requestPath.Equals("/json/version", StringComparison.OrdinalIgnoreCase))
{ {
var browserVersionJson = await GetBrowserVersionInfoAsync(); // VS Code's "js-debug" nightly extension, when configured to use the "pwa-chrome"
// debug type, uses the /json/version endpoint to find the websocket endpoint for
// debugging the browser that listens on a user-specified port.
//
// To make this flow work with the Mono debug proxy, we pass the request through
// to the underlying browser (to get its actual version info) but then overwrite
// the "webSocketDebuggerUrl" with the URL to the proxy.
//
// This whole connection flow isn't very good because it doesn't have any way
// to specify the debug port for the underlying browser. So, we end up assuming
// the default port 9222 in all cases. This is good enough for a manual "attach"
// but isn't good enough if the IDE is responsible for launching the browser,
// as it will be on a random port. So,
//
// - VS isn't going to use this. Instead it will use a configured "debugEndpoint"
// property from which it can construct the proxy URL directly (including adding
// a "browser" querystring value to specify the underlying endpoint), bypassing
// /json/version altogether
// - We will need to update the VS Code debug adapter to make it do the same as VS
// if there is a "debugEndpoint" property configured
//
// Once both VS and VS Code support the "debugEndpoint" flow, we should be able to
// remove this /json/version code altogether. We should check that in-browser
// debugging still works at that point.
var browserVersionJsonStream = await GetBrowserVersionInfoAsync();
var browserVersion = await JsonSerializer.DeserializeAsync<Dictionary<string, object>>(browserVersionJsonStream);
if (browserVersion.TryGetValue("webSocketDebuggerUrl", out var browserEndpoint))
{
var proxyEndpoint = GetProxyEndpoint(request, ((JsonElement)browserEndpoint).GetString());
browserVersion["webSocketDebuggerUrl"] = proxyEndpoint;
}
context.Response.ContentType = "application/json"; context.Response.ContentType = "application/json";
await context.Response.WriteAsync(browserVersionJson); await JsonSerializer.SerializeAsync(context.Response.Body, browserVersion);
} }
} }
else else
@ -142,7 +177,7 @@ namespace Microsoft.AspNetCore.Builder
}); });
} }
private static async Task DebugWebSocketProxyRequest(HttpContext context) private static async Task DebugWebSocketProxyRequest(ILoggerFactory loggerFactory, HttpContext context)
{ {
if (!context.WebSockets.IsWebSocketRequest) if (!context.WebSockets.IsWebSocketRequest)
{ {
@ -152,7 +187,7 @@ namespace Microsoft.AspNetCore.Builder
var browserUri = new Uri(context.Request.Query["browser"]); var browserUri = new Uri(context.Request.Query["browser"]);
var ideSocket = await context.WebSockets.AcceptWebSocketAsync(); var ideSocket = await context.WebSockets.AcceptWebSocketAsync();
await new MonoProxy().Run(browserUri, ideSocket); await new MonoProxy(loggerFactory).Run(browserUri, ideSocket);
} }
private static async Task DebugHome(HttpContext context) private static async Task DebugHome(HttpContext context)
@ -235,13 +270,30 @@ namespace Microsoft.AspNetCore.Builder
// page and redirect there // page and redirect there
var tabToDebug = matchingTabs.Single(); var tabToDebug = matchingTabs.Single();
var underlyingV8Endpoint = tabToDebug.WebSocketDebuggerUrl; var underlyingV8Endpoint = tabToDebug.WebSocketDebuggerUrl;
var proxyEndpoint = $"{request.Host}{request.PathBase}/_framework/debug/ws-proxy?browser={WebUtility.UrlEncode(underlyingV8Endpoint)}"; var proxyEndpoint = GetProxyEndpoint(request, underlyingV8Endpoint);
var devToolsUrlAbsolute = new Uri(debuggerHost + tabToDebug.DevtoolsFrontendUrl); var devToolsUrlAbsolute = new Uri(debuggerHost + tabToDebug.DevtoolsFrontendUrl);
var wsParamName = request.IsHttps ? "wss" : "ws"; var devToolsUrlWithProxy = $"{devToolsUrlAbsolute.Scheme}://{devToolsUrlAbsolute.Authority}{devToolsUrlAbsolute.AbsolutePath}?{proxyEndpoint.Scheme}={proxyEndpoint.Authority}{proxyEndpoint.PathAndQuery}";
var devToolsUrlWithProxy = $"{devToolsUrlAbsolute.Scheme}://{devToolsUrlAbsolute.Authority}{devToolsUrlAbsolute.AbsolutePath}?{wsParamName}={proxyEndpoint}";
context.Response.Redirect(devToolsUrlWithProxy); context.Response.Redirect(devToolsUrlWithProxy);
} }
private static Uri GetProxyEndpoint(HttpRequest incomingRequest, string browserEndpoint)
{
var builder = new UriBuilder(
schemeName: incomingRequest.IsHttps ? "wss" : "ws",
hostName: incomingRequest.Host.Host)
{
Path = $"{incomingRequest.PathBase}/_framework/debug/ws-proxy",
Query = $"browser={WebUtility.UrlEncode(browserEndpoint)}"
};
if (incomingRequest.Host.Port.HasValue)
{
builder.Port = incomingRequest.Host.Port.Value;
}
return builder.Uri;
}
private static string GetLaunchChromeInstructions(string appRootUrl) private static string GetLaunchChromeInstructions(string appRootUrl)
{ {
var profilePath = Path.Combine(Path.GetTempPath(), "blazor-chrome-debug"); var profilePath = Path.Combine(Path.GetTempPath(), "blazor-chrome-debug");
@ -289,11 +341,12 @@ namespace Microsoft.AspNetCore.Builder
} }
} }
private static async Task<string> GetBrowserVersionInfoAsync() private static async Task<Stream> GetBrowserVersionInfoAsync()
{ {
using var httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(5) }; using var httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(5) };
var debuggerHost = GetDebuggerHost(); var debuggerHost = GetDebuggerHost();
return await httpClient.GetStringAsync($"{debuggerHost}/json/version"); var response = await httpClient.GetAsync($"{debuggerHost}/json/version");
return await response.Content.ReadAsStreamAsync();
} }
private static async Task<IEnumerable<BrowserTab>> GetOpenedBrowserTabs() private static async Task<IEnumerable<BrowserTab>> GetOpenedBrowserTabs()

View File

@ -9,8 +9,10 @@ using System.Net.Http;
using Mono.Cecil.Pdb; using Mono.Cecil.Pdb;
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Threading;
namespace WsProxy { namespace WebAssembly.Net.Debugging {
internal class BreakPointRequest { internal class BreakPointRequest {
public string Assembly { get; private set; } public string Assembly { get; private set; }
public string File { get; private set; } public string File { get; private set; }
@ -23,13 +25,15 @@ namespace WsProxy {
public static BreakPointRequest Parse (JObject args, DebugStore store) public static BreakPointRequest Parse (JObject args, DebugStore store)
{ {
if (args == null) // 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; return null;
var url = args? ["url"]?.Value<string> (); var url = args? ["url"]?.Value<string> ();
if (url == null) { if (url == null) {
var urlRegex = args?["urlRegex"].Value<string>(); var urlRegex = args?["urlRegex"].Value<string>();
var sourceFile = store.GetFileByUrlRegex (urlRegex); var sourceFile = store?.GetFileByUrlRegex (urlRegex);
url = sourceFile?.DotNetUrl; url = sourceFile?.DotNetUrl;
} }
@ -72,7 +76,6 @@ namespace WsProxy {
} }
} }
internal class VarInfo { internal class VarInfo {
public VarInfo (VariableDebugInformation v) public VarInfo (VariableDebugInformation v)
{ {
@ -85,10 +88,10 @@ namespace WsProxy {
this.Name = p.Name; this.Name = p.Name;
this.Index = (p.Index + 1) * -1; this.Index = (p.Index + 1) * -1;
} }
public string Name { get; private set; } public string Name { get; private set; }
public int Index { get; private set; } public int Index { get; private set; }
public override string ToString () public override string ToString ()
{ {
return $"(var-info [{Index}] '{Name}')"; return $"(var-info [{Index}] '{Name}')";
@ -97,18 +100,14 @@ namespace WsProxy {
internal class CliLocation { internal class CliLocation {
private MethodInfo method;
private int offset;
public CliLocation (MethodInfo method, int offset) public CliLocation (MethodInfo method, int offset)
{ {
this.method = method; Method = method;
this.offset = offset; Offset = offset;
} }
public MethodInfo Method { get => method; } public MethodInfo Method { get; private set; }
public int Offset { get => offset; } public int Offset { get; private set; }
} }
@ -282,7 +281,6 @@ namespace WsProxy {
.Where (v => !v.IsDebuggerHidden) .Where (v => !v.IsDebuggerHidden)
.Select (v => new VarInfo (v))); .Select (v => new VarInfo (v)));
return res.ToArray (); return res.ToArray ();
} }
} }
@ -294,20 +292,21 @@ namespace WsProxy {
Dictionary<int, MethodInfo> methods = new Dictionary<int, MethodInfo> (); Dictionary<int, MethodInfo> methods = new Dictionary<int, MethodInfo> ();
Dictionary<string, string> sourceLinkMappings = new Dictionary<string, string>(); Dictionary<string, string> sourceLinkMappings = new Dictionary<string, string>();
readonly List<SourceFile> sources = new List<SourceFile>(); readonly List<SourceFile> sources = new List<SourceFile>();
internal string Url { get; private set; }
public AssemblyInfo (byte[] assembly, byte[] pdb) public AssemblyInfo (string url, byte[] assembly, byte[] pdb)
{ {
lock (typeof (AssemblyInfo)) { lock (typeof (AssemblyInfo)) {
this.id = ++next_id; this.id = ++next_id;
} }
try { try {
Url = url;
ReaderParameters rp = new ReaderParameters (/*ReadingMode.Immediate*/); ReaderParameters rp = new ReaderParameters (/*ReadingMode.Immediate*/);
if (pdb != null) { rp.ReadSymbols = true;
rp.ReadSymbols = true; rp.SymbolReaderProvider = new PdbReaderProvider ();
rp.SymbolReaderProvider = new PortablePdbReaderProvider (); if (pdb != null)
rp.SymbolStream = new MemoryStream (pdb); rp.SymbolStream = new MemoryStream (pdb);
}
rp.ReadingMode = ReadingMode.Immediate; rp.ReadingMode = ReadingMode.Immediate;
rp.InMemory = true; rp.InMemory = true;
@ -315,13 +314,16 @@ namespace WsProxy {
this.image = ModuleDefinition.ReadModule (new MemoryStream (assembly), rp); this.image = ModuleDefinition.ReadModule (new MemoryStream (assembly), rp);
} catch (BadImageFormatException ex) { } catch (BadImageFormatException ex) {
Console.WriteLine ($"Failed to read assembly as portable PDB: {ex.Message}"); Console.WriteLine ($"Failed to read assembly as portable PDB: {ex.Message}");
} catch (ArgumentNullException) {
if (pdb != null)
throw;
} }
if (this.image == null) { if (this.image == null) {
ReaderParameters rp = new ReaderParameters (/*ReadingMode.Immediate*/); ReaderParameters rp = new ReaderParameters (/*ReadingMode.Immediate*/);
if (pdb != null) { if (pdb != null) {
rp.ReadSymbols = true; rp.ReadSymbols = true;
rp.SymbolReaderProvider = new NativePdbReaderProvider (); rp.SymbolReaderProvider = new PdbReaderProvider ();
rp.SymbolStream = new MemoryStream (pdb); rp.SymbolStream = new MemoryStream (pdb);
} }
@ -491,61 +493,72 @@ namespace WsProxy {
} }
internal class DebugStore { internal class DebugStore {
// MonoProxy proxy; - commenting out because never gets assigned
List<AssemblyInfo> assemblies = new List<AssemblyInfo> (); List<AssemblyInfo> assemblies = new List<AssemblyInfo> ();
HttpClient client = new HttpClient ();
public DebugStore (string [] loaded_files) class DebugItem {
public string Url { get; set; }
public Task<byte[][]> Data { get; set; }
}
public async Task Load (SessionId sessionId, string [] loaded_files, CancellationToken token)
{ {
bool MatchPdb (string asm, string pdb) static bool MatchPdb (string asm, string pdb)
{ => Path.ChangeExtension (asm, "pdb") == pdb;
return Path.ChangeExtension (asm, "pdb") == pdb;
}
var asm_files = new List<string> (); var asm_files = new List<string> ();
var pdb_files = new List<string> (); var pdb_files = new List<string> ();
foreach (var f in loaded_files) { foreach (var file_name in loaded_files) {
var file_name = f;
if (file_name.EndsWith (".pdb", StringComparison.OrdinalIgnoreCase)) if (file_name.EndsWith (".pdb", StringComparison.OrdinalIgnoreCase))
pdb_files.Add (file_name); pdb_files.Add (file_name);
else else
asm_files.Add (file_name); asm_files.Add (file_name);
} }
//FIXME make this parallel List<DebugItem> steps = new List<DebugItem> ();
foreach (var p in asm_files) { foreach (var url in asm_files) {
try { try {
var pdb = pdb_files.FirstOrDefault (n => MatchPdb (p, n)); var pdb = pdb_files.FirstOrDefault (n => MatchPdb (url, n));
HttpClient h = new HttpClient (); steps.Add (
var assembly_bytes = h.GetByteArrayAsync (p).Result; new DebugItem {
byte [] pdb_bytes = null; Url = url,
if (pdb != null) Data = Task.WhenAll (client.GetByteArrayAsync (url), pdb != null ? client.GetByteArrayAsync (pdb) : Task.FromResult<byte []> (null))
pdb_bytes = h.GetByteArrayAsync (pdb).Result; });
this.assemblies.Add (new AssemblyInfo (assembly_bytes, pdb_bytes));
} catch (Exception e) { } catch (Exception e) {
Console.WriteLine ($"Failed to read {p} ({e.Message})"); Console.WriteLine ($"Failed to read {url} ({e.Message})");
var o = JObject.FromObject (new {
entry = new {
source = "other",
level = "warning",
text = $"Failed to read {url} ({e.Message})"
}
});
// proxy.SendEvent (sessionId, "Log.entryAdded", o, token); - commenting out because `proxy` would always be null
}
}
foreach (var step in steps) {
try {
var bytes = await step.Data;
assemblies.Add (new AssemblyInfo (step.Url, bytes[0], bytes[1]));
} catch (Exception e) {
Console.WriteLine ($"Failed to Load {step.Url} ({e.Message})");
} }
} }
} }
public IEnumerable<SourceFile> AllSources () public IEnumerable<SourceFile> AllSources ()
{ => assemblies.SelectMany (a => a.Sources);
foreach (var a in assemblies) {
foreach (var s in a.Sources)
yield return s;
}
}
public SourceFile GetFileById (SourceId id) public SourceFile GetFileById (SourceId id)
{ => AllSources ().FirstOrDefault (f => f.SourceId.Equals (id));
return AllSources ().FirstOrDefault (f => f.SourceId.Equals (id));
}
public AssemblyInfo GetAssemblyByName (string name) public AssemblyInfo GetAssemblyByName (string name)
{ => assemblies.FirstOrDefault (a => a.Name.Equals (name, StringComparison.InvariantCultureIgnoreCase));
return assemblies.FirstOrDefault (a => a.Name.Equals (name, StringComparison.InvariantCultureIgnoreCase));
}
/* /*
V8 uses zero based indexing for both line and column. V8 uses zero based indexing for both line and column.
PPDBs uses one based indexing for both line and column. PPDBs uses one based indexing for both line and column.
*/ */
@ -598,7 +611,7 @@ namespace WsProxy {
PPDBs uses one based indexing for both line and column. PPDBs uses one based indexing for both line and column.
*/ */
static bool Match (SequencePoint sp, int line, int column) static bool Match (SequencePoint sp, int line, int column)
{ {
var bp = (line: line + 1, column: column + 1); var bp = (line: line + 1, column: column + 1);
if (sp.StartLine > bp.line || sp.EndLine < bp.line) if (sp.StartLine > bp.line || sp.EndLine < bp.line)

View File

@ -0,0 +1,139 @@
using System;
using System.Threading.Tasks;
using System.Net.WebSockets;
using System.Threading;
using System.IO;
using System.Text;
using System.Collections.Generic;
namespace WebAssembly.Net.Debugging {
internal class DevToolsClient: IDisposable {
ClientWebSocket socket;
List<Task> pending_ops = new List<Task> ();
TaskCompletionSource<bool> side_exit = new TaskCompletionSource<bool> ();
List<byte []> pending_writes = new List<byte []> ();
Task current_write;
public DevToolsClient () {
}
~DevToolsClient() {
Dispose(false);
}
public void Dispose() {
Dispose(true);
}
public async Task Close (CancellationToken cancellationToken)
{
if (socket.State == WebSocketState.Open)
await socket.CloseOutputAsync (WebSocketCloseStatus.NormalClosure, "Closing", cancellationToken);
}
protected virtual void Dispose (bool disposing) {
if (disposing)
socket.Dispose ();
}
Task Pump (Task task, CancellationToken token)
{
if (task != current_write)
return null;
current_write = null;
pending_writes.RemoveAt (0);
if (pending_writes.Count > 0) {
current_write = socket.SendAsync (new ArraySegment<byte> (pending_writes [0]), WebSocketMessageType.Text, true, token);
return current_write;
}
return null;
}
async Task<string> ReadOne (CancellationToken token)
{
byte [] buff = new byte [4000];
var mem = new MemoryStream ();
while (true) {
var result = await this.socket.ReceiveAsync (new ArraySegment<byte> (buff), token);
if (result.MessageType == WebSocketMessageType.Close) {
return null;
}
if (result.EndOfMessage) {
mem.Write (buff, 0, result.Count);
return Encoding.UTF8.GetString (mem.GetBuffer (), 0, (int)mem.Length);
} else {
mem.Write (buff, 0, result.Count);
}
}
}
protected void Send (byte [] bytes, CancellationToken token)
{
pending_writes.Add (bytes);
if (pending_writes.Count == 1) {
if (current_write != null)
throw new Exception ("Internal state is bad. current_write must be null if there are no pending writes");
current_write = socket.SendAsync (new ArraySegment<byte> (bytes), WebSocketMessageType.Text, true, token);
pending_ops.Add (current_write);
}
}
async Task MarkCompleteAfterward (Func<CancellationToken, Task> send, CancellationToken token)
{
try {
await send(token);
side_exit.SetResult (true);
} catch (Exception e) {
side_exit.SetException (e);
}
}
protected async Task<bool> ConnectWithMainLoops(
Uri uri,
Func<string, CancellationToken, Task> receive,
Func<CancellationToken, Task> send,
CancellationToken token) {
Console.WriteLine ("connecting to {0}", uri);
this.socket = new ClientWebSocket ();
this.socket.Options.KeepAliveInterval = Timeout.InfiniteTimeSpan;
await this.socket.ConnectAsync (uri, token);
pending_ops.Add (ReadOne (token));
pending_ops.Add (side_exit.Task);
pending_ops.Add (MarkCompleteAfterward (send, token));
while (!token.IsCancellationRequested) {
var task = await Task.WhenAny (pending_ops);
if (task == pending_ops [0]) { //pending_ops[0] is for message reading
var msg = ((Task<string>)task).Result;
pending_ops [0] = ReadOne (token);
Task tsk = receive (msg, token);
if (tsk != null)
pending_ops.Add (tsk);
} else if (task == pending_ops [1]) {
var res = ((Task<bool>)task).Result;
//it might not throw if exiting successfull
return res;
} else { //must be a background task
pending_ops.Remove (task);
var tsk = Pump (task, token);
if (tsk != null)
pending_ops.Add (tsk);
}
}
return false;
}
protected virtual void Log (string priority, string msg)
{
//
}
}
}

View File

@ -8,8 +8,16 @@ using System.Threading;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.Extensions.Logging;
namespace WsProxy { namespace WebAssembly.Net.Debugging {
internal class SessionId {
public string sessionId;
}
internal class MessageId : SessionId {
public int id;
}
internal struct Result { internal struct Result {
public JObject Value { get; private set; } public JObject Value { get; private set; }
@ -26,6 +34,7 @@ namespace WsProxy {
public static Result FromJson (JObject obj) public static Result FromJson (JObject obj)
{ {
//Log ("protocol", $"from result: {obj}");
return new Result (obj ["result"] as JObject, obj ["error"] as JObject); return new Result (obj ["result"] as JObject, obj ["error"] as JObject);
} }
@ -39,28 +48,30 @@ namespace WsProxy {
return new Result (null, err); return new Result (null, err);
} }
public JObject ToJObject (int id) { public JObject ToJObject (MessageId target) {
if (IsOk) { if (IsOk) {
return JObject.FromObject (new { return JObject.FromObject (new {
id = id, target.id,
target.sessionId,
result = Value result = Value
}); });
} else { } else {
return JObject.FromObject (new { return JObject.FromObject (new {
id = id, target.id,
target.sessionId,
error = Error error = Error
}); });
} }
} }
} }
class WsQueue { class DevToolsQueue {
Task current_send; Task current_send;
List<byte []> pending; List<byte []> pending;
public WebSocket Ws { get; private set; } public WebSocket Ws { get; private set; }
public Task CurrentSend { get { return current_send; } } public Task CurrentSend { get { return current_send; } }
public WsQueue (WebSocket sock) public DevToolsQueue (WebSocket sock)
{ {
this.Ws = sock; this.Ws = sock;
pending = new List<byte []> (); pending = new List<byte []> ();
@ -71,7 +82,7 @@ namespace WsProxy {
pending.Add (bytes); pending.Add (bytes);
if (pending.Count == 1) { if (pending.Count == 1) {
if (current_send != null) if (current_send != null)
throw new Exception ("UNEXPECTED, current_send MUST BE NULL IF THERE'S no pending send"); throw new Exception ("current_send MUST BE NULL IF THERE'S no pending send");
//Console.WriteLine ("sending {0} bytes", bytes.Length); //Console.WriteLine ("sending {0} bytes", bytes.Length);
current_send = Ws.SendAsync (new ArraySegment<byte> (bytes), WebSocketMessageType.Text, true, token); current_send = Ws.SendAsync (new ArraySegment<byte> (bytes), WebSocketMessageType.Text, true, token);
return current_send; return current_send;
@ -86,7 +97,7 @@ namespace WsProxy {
if (pending.Count > 0) { if (pending.Count > 0) {
if (current_send != null) if (current_send != null)
throw new Exception ("UNEXPECTED, current_send MUST BE NULL IF THERE'S no pending send"); throw new Exception ("current_send MUST BE NULL IF THERE'S no pending send");
//Console.WriteLine ("sending more {0} bytes", pending[0].Length); //Console.WriteLine ("sending more {0} bytes", pending[0].Length);
current_send = Ws.SendAsync (new ArraySegment<byte> (pending [0]), WebSocketMessageType.Text, true, token); current_send = Ws.SendAsync (new ArraySegment<byte> (pending [0]), WebSocketMessageType.Text, true, token);
return current_send; return current_send;
@ -95,22 +106,28 @@ namespace WsProxy {
} }
} }
internal class WsProxy { internal class DevToolsProxy {
TaskCompletionSource<bool> side_exception = new TaskCompletionSource<bool> (); TaskCompletionSource<bool> side_exception = new TaskCompletionSource<bool> ();
TaskCompletionSource<bool> client_initiated_close = new TaskCompletionSource<bool> (); TaskCompletionSource<bool> client_initiated_close = new TaskCompletionSource<bool> ();
List<(int, TaskCompletionSource<Result>)> pending_cmds = new List<(int, TaskCompletionSource<Result>)> (); List<(MessageId, TaskCompletionSource<Result>)> pending_cmds = new List<(MessageId, TaskCompletionSource<Result>)> ();
ClientWebSocket browser; ClientWebSocket browser;
WebSocket ide; WebSocket ide;
int next_cmd_id; int next_cmd_id;
List<Task> pending_ops = new List<Task> (); List<Task> pending_ops = new List<Task> ();
List<WsQueue> queues = new List<WsQueue> (); List<DevToolsQueue> queues = new List<DevToolsQueue> ();
readonly ILogger logger;
protected virtual Task<bool> AcceptEvent (string method, JObject args, CancellationToken token) public DevToolsProxy(ILoggerFactory loggerFactory)
{
logger = loggerFactory.CreateLogger<DevToolsProxy>();
}
protected virtual Task<bool> AcceptEvent (SessionId sessionId, string method, JObject args, CancellationToken token)
{ {
return Task.FromResult (false); return Task.FromResult (false);
} }
protected virtual Task<bool> AcceptCommand (int id, string method, JObject args, CancellationToken token) protected virtual Task<bool> AcceptCommand (MessageId id, string method, JObject args, CancellationToken token)
{ {
return Task.FromResult (false); return Task.FromResult (false);
} }
@ -122,7 +139,7 @@ namespace WsProxy {
while (true) { while (true) {
if (socket.State != WebSocketState.Open) { if (socket.State != WebSocketState.Open) {
Console.WriteLine ($"WSProxy: Socket is no longer open."); Log ("error", $"DevToolsProxy: Socket is no longer open.");
client_initiated_close.TrySetResult (true); client_initiated_close.TrySetResult (true);
return null; return null;
} }
@ -133,51 +150,53 @@ namespace WsProxy {
return null; return null;
} }
if (result.EndOfMessage) { mem.Write (buff, 0, result.Count);
mem.Write (buff, 0, result.Count);
if (result.EndOfMessage)
return Encoding.UTF8.GetString (mem.GetBuffer (), 0, (int)mem.Length); return Encoding.UTF8.GetString (mem.GetBuffer (), 0, (int)mem.Length);
} else {
mem.Write (buff, 0, result.Count);
}
} }
} }
WsQueue GetQueueForSocket (WebSocket ws) DevToolsQueue GetQueueForSocket (WebSocket ws)
{ {
return queues.FirstOrDefault (q => q.Ws == ws); return queues.FirstOrDefault (q => q.Ws == ws);
} }
WsQueue GetQueueForTask (Task task) { DevToolsQueue GetQueueForTask (Task task)
{
return queues.FirstOrDefault (q => q.CurrentSend == task); return queues.FirstOrDefault (q => q.CurrentSend == task);
} }
void Send (WebSocket to, JObject o, CancellationToken token) void Send (WebSocket to, JObject o, CancellationToken token)
{ {
var bytes = Encoding.UTF8.GetBytes (o.ToString ()); var sender = browser == to ? "Send-browser" : "Send-ide";
Log ("protocol", $"{sender}: {o}");
var bytes = Encoding.UTF8.GetBytes (o.ToString ());
var queue = GetQueueForSocket (to); var queue = GetQueueForSocket (to);
var task = queue.Send (bytes, token); var task = queue.Send (bytes, token);
if (task != null) if (task != null)
pending_ops.Add (task); pending_ops.Add (task);
} }
async Task OnEvent (string method, JObject args, CancellationToken token) async Task OnEvent (SessionId sessionId, string method, JObject args, CancellationToken token)
{ {
try { try {
if (!await AcceptEvent (method, args, token)) { if (!await AcceptEvent (sessionId, method, args, token)) {
//Console.WriteLine ("proxy browser: {0}::{1}",method, args); //Console.WriteLine ("proxy browser: {0}::{1}",method, args);
SendEventInternal (method, args, token); SendEventInternal (sessionId, method, args, token);
} }
} catch (Exception e) { } catch (Exception e) {
side_exception.TrySetException (e); side_exception.TrySetException (e);
} }
} }
async Task OnCommand (int id, string method, JObject args, CancellationToken token) async Task OnCommand (MessageId id, string method, JObject args, CancellationToken token)
{ {
try { try {
if (!await AcceptCommand (id, method, args, token)) { if (!await AcceptCommand (id, method, args, token)) {
var res = await SendCommandInternal (method, args, token); var res = await SendCommandInternal (id, method, args, token);
SendResponseInternal (id, res, token); SendResponseInternal (id, res, token);
} }
} catch (Exception e) { } catch (Exception e) {
@ -185,10 +204,11 @@ namespace WsProxy {
} }
} }
void OnResponse (int id, Result result) void OnResponse (MessageId id, Result result)
{ {
//Console.WriteLine ("got id {0} res {1}", id, result); //Console.WriteLine ("got id {0} res {1}", id, result);
var idx = pending_cmds.FindIndex (e => e.Item1 == id); // Fixme
var idx = pending_cmds.FindIndex (e => e.Item1.id == id.id && e.Item1.sessionId == id.sessionId);
var item = pending_cmds [idx]; var item = pending_cmds [idx];
pending_cmds.RemoveAt (idx); pending_cmds.RemoveAt (idx);
@ -197,68 +217,74 @@ namespace WsProxy {
void ProcessBrowserMessage (string msg, CancellationToken token) void ProcessBrowserMessage (string msg, CancellationToken token)
{ {
// Debug ($"browser: {msg}"); Log ("protocol", $"browser: {msg}");
var res = JObject.Parse (msg); var res = JObject.Parse (msg);
if (res ["id"] == null) if (res ["id"] == null)
pending_ops.Add (OnEvent (res ["method"].Value<string> (), res ["params"] as JObject, token)); pending_ops.Add (OnEvent (new SessionId { sessionId = res ["sessionId"]?.Value<string> () }, res ["method"].Value<string> (), res ["params"] as JObject, token));
else else
OnResponse (res ["id"].Value<int> (), Result.FromJson (res)); OnResponse (new MessageId { id = res ["id"].Value<int> (), sessionId = res ["sessionId"]?.Value<string> () }, Result.FromJson (res));
} }
void ProcessIdeMessage (string msg, CancellationToken token) void ProcessIdeMessage (string msg, CancellationToken token)
{ {
Log ("protocol", $"ide: {msg}");
if (!string.IsNullOrEmpty (msg)) { if (!string.IsNullOrEmpty (msg)) {
var res = JObject.Parse (msg); var res = JObject.Parse (msg);
pending_ops.Add (OnCommand (res ["id"].Value<int> (), res ["method"].Value<string> (), res ["params"] as JObject, token)); pending_ops.Add (OnCommand (new MessageId { id = res ["id"].Value<int> (), sessionId = res ["sessionId"]?.Value<string> () }, res ["method"].Value<string> (), res ["params"] as JObject, token));
} }
} }
internal async Task<Result> SendCommand (string method, JObject args, CancellationToken token) { internal async Task<Result> SendCommand (SessionId id, string method, JObject args, CancellationToken token) {
// Debug ($"sending command {method}: {args}"); //Log ("verbose", $"sending command {method}: {args}");
return await SendCommandInternal (method, args, token); return await SendCommandInternal (id, method, args, token);
} }
Task<Result> SendCommandInternal (string method, JObject args, CancellationToken token) Task<Result> SendCommandInternal (SessionId sessionId, string method, JObject args, CancellationToken token)
{ {
int id = ++next_cmd_id; int id = ++next_cmd_id;
var o = JObject.FromObject (new { var o = JObject.FromObject (new {
id = id, sessionId.sessionId,
method = method, id,
method,
@params = args @params = args
}); });
var tcs = new TaskCompletionSource<Result> (); var tcs = new TaskCompletionSource<Result> ();
//Console.WriteLine ("add cmd id {0}", id);
pending_cmds.Add ((id, tcs));
var msgId = new MessageId { id = id, sessionId = sessionId.sessionId };
//Log ("verbose", $"add cmd id {sessionId}-{id}");
pending_cmds.Add ((msgId , tcs));
Send (this.browser, o, token); Send (this.browser, o, token);
return tcs.Task; return tcs.Task;
} }
public void SendEvent (string method, JObject args, CancellationToken token) public void SendEvent (SessionId sessionId, string method, JObject args, CancellationToken token)
{ {
//Debug ($"sending event {method}: {args}"); //Log ("verbose", $"sending event {method}: {args}");
SendEventInternal (method, args, token); SendEventInternal (sessionId, method, args, token);
} }
void SendEventInternal (string method, JObject args, CancellationToken token) void SendEventInternal (SessionId sessionId, string method, JObject args, CancellationToken token)
{ {
var o = JObject.FromObject (new { var o = JObject.FromObject (new {
method = method, sessionId.sessionId,
method,
@params = args @params = args
}); });
Send (this.ide, o, token); Send (this.ide, o, token);
} }
internal void SendResponse (int id, Result result, CancellationToken token) internal void SendResponse (MessageId id, Result result, CancellationToken token)
{ {
//Debug ($"sending response: {id}: {result.ToJObject (id)}"); //Log ("verbose", $"sending response: {id}: {result.ToJObject (id)}");
SendResponseInternal (id, result, token); SendResponseInternal (id, result, token);
} }
void SendResponseInternal (int id, Result result, CancellationToken token) void SendResponseInternal (MessageId id, Result result, CancellationToken token)
{ {
JObject o = result.ToJObject (id); JObject o = result.ToJObject (id);
@ -268,16 +294,16 @@ namespace WsProxy {
// , HttpContext context) // , HttpContext context)
public async Task Run (Uri browserUri, WebSocket ideSocket) public async Task Run (Uri browserUri, WebSocket ideSocket)
{ {
Debug ($"WsProxy Starting on {browserUri}"); Log ("info", $"DevToolsProxy: Starting on {browserUri}");
using (this.ide = ideSocket) { using (this.ide = ideSocket) {
Debug ($"WsProxy: IDE waiting for connection on {browserUri}"); Log ("verbose", $"DevToolsProxy: IDE waiting for connection on {browserUri}");
queues.Add (new WsQueue (this.ide)); queues.Add (new DevToolsQueue (this.ide));
using (this.browser = new ClientWebSocket ()) { using (this.browser = new ClientWebSocket ()) {
this.browser.Options.KeepAliveInterval = Timeout.InfiniteTimeSpan; this.browser.Options.KeepAliveInterval = Timeout.InfiniteTimeSpan;
await this.browser.ConnectAsync (browserUri, CancellationToken.None); await this.browser.ConnectAsync (browserUri, CancellationToken.None);
queues.Add (new WsQueue (this.browser)); queues.Add (new DevToolsQueue (this.browser));
Debug ($"WsProxy: Client connected on {browserUri}"); Log ("verbose", $"DevToolsProxy: Client connected on {browserUri}");
var x = new CancellationTokenSource (); var x = new CancellationTokenSource ();
pending_ops.Add (ReadOne (browser, x.Token)); pending_ops.Add (ReadOne (browser, x.Token));
@ -306,7 +332,7 @@ namespace WsProxy {
throw new Exception ("side task must always complete with an exception, what's going on???"); throw new Exception ("side task must always complete with an exception, what's going on???");
} else if (task == pending_ops [3]) { } else if (task == pending_ops [3]) {
var res = ((Task<bool>)task).Result; var res = ((Task<bool>)task).Result;
Debug ($"WsProxy: Client initiated close from {browserUri}"); Log ("verbose", $"DevToolsProxy: Client initiated close from {browserUri}");
x.Cancel (); x.Cancel ();
} else { } else {
//must be a background task //must be a background task
@ -320,7 +346,7 @@ namespace WsProxy {
} }
} }
} catch (Exception e) { } catch (Exception e) {
Debug ($"WsProxy::Run: Exception {e}"); Log ("error", $"DevToolsProxy::Run: Exception {e}");
//throw; //throw;
} finally { } finally {
if (!x.IsCancellationRequested) if (!x.IsCancellationRequested)
@ -330,14 +356,28 @@ namespace WsProxy {
} }
} }
protected void Debug (string msg) protected void Log (string priority, string msg)
{ {
Console.WriteLine (msg); switch (priority) {
} case "protocol":
logger.LogTrace (msg);
protected void Info (string msg) break;
{ case "verbose":
Console.WriteLine (msg); logger.LogDebug (msg);
break;
case "info":
logger.LogInformation(msg);
break;
case "warning":
logger.LogWarning(msg);
break;
case "error":
logger.LogError (msg);
break;
default:
logger.LogError(msg);
break;
}
} }
} }
} }

View File

@ -3,14 +3,13 @@ using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using System.Net.WebSockets;
using System.Threading; using System.Threading;
using System.IO; using System.IO;
using System.Text;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net; using System.Net;
using Microsoft.Extensions.Logging;
namespace WsProxy { namespace WebAssembly.Net.Debugging {
internal class MonoCommands { internal class MonoCommands {
public const string GET_CALL_STACK = "MONO.mono_wasm_get_call_stack()"; public const string GET_CALL_STACK = "MONO.mono_wasm_get_call_stack()";
@ -29,10 +28,10 @@ namespace WsProxy {
BpNotFound = 100000, BpNotFound = 100000,
} }
internal class MonoConstants { internal class MonoConstants {
public const string RUNTIME_IS_READY = "mono_wasm_runtime_ready"; public const string RUNTIME_IS_READY = "mono_wasm_runtime_ready";
} }
class Frame { class Frame {
public Frame (MethodInfo method, SourceLocation location, int id) public Frame (MethodInfo method, SourceLocation location, int id)
{ {
@ -73,7 +72,7 @@ namespace WsProxy {
Over Over
} }
internal class MonoProxy : WsProxy { internal class MonoProxy : DevToolsProxy {
DebugStore store; DebugStore store;
List<Breakpoint> breakpoints = new List<Breakpoint> (); List<Breakpoint> breakpoints = new List<Breakpoint> ();
List<Frame> current_callstack; List<Frame> current_callstack;
@ -82,9 +81,9 @@ namespace WsProxy {
int ctx_id; int ctx_id;
JObject aux_ctx_data; JObject aux_ctx_data;
public MonoProxy () { } public MonoProxy (ILoggerFactory loggerFactory) : base(loggerFactory) { }
protected override async Task<bool> AcceptEvent (string method, JObject args, CancellationToken token) protected override async Task<bool> AcceptEvent (SessionId sessionId, string method, JObject args, CancellationToken token)
{ {
switch (method) { switch (method) {
case "Runtime.executionContextCreated": { case "Runtime.executionContextCreated": {
@ -93,8 +92,8 @@ namespace WsProxy {
if (aux_data != null) { if (aux_data != null) {
var is_default = aux_data ["isDefault"]?.Value<bool> (); var is_default = aux_data ["isDefault"]?.Value<bool> ();
if (is_default == true) { if (is_default == true) {
var ctx_id = ctx ["id"].Value<int> (); var id = new MessageId { id = ctx ["id"].Value<int> (), sessionId = sessionId.sessionId };
await OnDefaultContext (ctx_id, aux_data, token); await OnDefaultContext (id, aux_data, token);
} }
} }
break; break;
@ -103,11 +102,11 @@ namespace WsProxy {
//TODO figure out how to stich out more frames and, in particular what happens when real wasm is on the stack //TODO figure out how to stich out more frames and, in particular what happens when real wasm is on the stack
var top_func = args? ["callFrames"]? [0]? ["functionName"]?.Value<string> (); var top_func = args? ["callFrames"]? [0]? ["functionName"]?.Value<string> ();
if (top_func == "mono_wasm_fire_bp" || top_func == "_mono_wasm_fire_bp") { if (top_func == "mono_wasm_fire_bp" || top_func == "_mono_wasm_fire_bp") {
await OnBreakPointHit (args, token); await OnBreakPointHit (sessionId, args, token);
return true; return true;
} }
if (top_func == MonoConstants.RUNTIME_IS_READY) { if (top_func == MonoConstants.RUNTIME_IS_READY) {
await OnRuntimeReady (token); await OnRuntimeReady (new SessionId { sessionId = sessionId.sessionId }, token);
return true; return true;
} }
break; break;
@ -119,24 +118,33 @@ namespace WsProxy {
} }
break; break;
} }
case "Debugger.enabled": {
await LoadStore (new SessionId { sessionId = args? ["sessionId"]?.Value<string> () }, token);
break;
}
} }
return false; return false;
} }
protected override async Task<bool> AcceptCommand (int id, string method, JObject args, CancellationToken token) protected override async Task<bool> AcceptCommand (MessageId id, string method, JObject args, CancellationToken token)
{ {
switch (method) { switch (method) {
case "Target.attachToTarget": {
break;
}
case "Target.attachToBrowserTarget": {
break;
}
case "Debugger.getScriptSource": { case "Debugger.getScriptSource": {
var script_id = args? ["scriptId"]?.Value<string> (); var script_id = args? ["scriptId"]?.Value<string> ();
if (script_id.StartsWith ("dotnet://", StringComparison.InvariantCultureIgnoreCase)) { if (script_id.StartsWith ("dotnet://", StringComparison.InvariantCultureIgnoreCase)) {
await OnGetScriptSource (id, script_id, token); await OnGetScriptSource (id, script_id, token);
return true; return true;
} }
break; break;
} }
case "Runtime.compileScript": { case "Runtime.compileScript": {
var exp = args? ["expression"]?.Value<string> (); var exp = args? ["expression"]?.Value<string> ();
if (exp.StartsWith ("//dotnet:", StringComparison.InvariantCultureIgnoreCase)) { if (exp.StartsWith ("//dotnet:", StringComparison.InvariantCultureIgnoreCase)) {
@ -156,7 +164,7 @@ namespace WsProxy {
} }
case "Debugger.setBreakpointByUrl": { case "Debugger.setBreakpointByUrl": {
Info ($"BP req {args}"); Log ("info", $"BP req {args}");
var bp_req = BreakPointRequest.Parse (args, store); var bp_req = BreakPointRequest.Parse (args, store);
if (bp_req != null) { if (bp_req != null) {
await SetBreakPoint (id, bp_req, token); await SetBreakPoint (id, bp_req, token);
@ -164,9 +172,10 @@ namespace WsProxy {
} }
break; break;
} }
case "Debugger.removeBreakpoint": { case "Debugger.removeBreakpoint": {
return await RemoveBreakpoint (id, args, token); return await RemoveBreakpoint (id, args, token);
} }
case "Debugger.resume": { case "Debugger.resume": {
await OnResume (token); await OnResume (token);
@ -199,16 +208,24 @@ namespace WsProxy {
case "Runtime.getProperties": { case "Runtime.getProperties": {
var objId = args? ["objectId"]?.Value<string> (); var objId = args? ["objectId"]?.Value<string> ();
if (objId.StartsWith ("dotnet:scope:", StringComparison.InvariantCulture)) { if (objId.StartsWith ("dotnet:")) {
await GetScopeProperties (id, int.Parse (objId.Substring ("dotnet:scope:".Length)), token); var parts = objId.Split (new char [] { ':' });
return true; if (parts.Length < 3)
} return true;
if (objId.StartsWith("dotnet:", StringComparison.InvariantCulture)) switch (parts[1]) {
{ case "scope": {
if (objId.StartsWith("dotnet:object:", StringComparison.InvariantCulture)) await GetScopeProperties (id, int.Parse (parts[2]), token);
await GetDetails(id, int.Parse(objId.Substring("dotnet:object:".Length)), token, MonoCommands.GET_OBJECT_PROPERTIES); break;
if (objId.StartsWith("dotnet:array:", StringComparison.InvariantCulture)) }
await GetDetails(id, int.Parse(objId.Substring("dotnet:array:".Length)), token, MonoCommands.GET_ARRAY_VALUES); case "object": {
await GetDetails (id, int.Parse (parts[2]), token, MonoCommands.GET_OBJECT_PROPERTIES);
break;
}
case "array": {
await GetDetails (id, int.Parse (parts[2]), token, MonoCommands.GET_ARRAY_VALUES);
break;
}
}
return true; return true;
} }
break; break;
@ -218,15 +235,16 @@ namespace WsProxy {
return false; return false;
} }
async Task OnRuntimeReady (CancellationToken token) async Task OnRuntimeReady (SessionId sessionId, CancellationToken token)
{ {
Info ("RUNTIME READY, PARTY TIME"); Log ("info", "RUNTIME READY, PARTY TIME");
await RuntimeReady (token); await RuntimeReady (sessionId, token);
await SendCommand ("Debugger.resume", new JObject (), token); await SendCommand (sessionId, "Debugger.resume", new JObject (), token);
SendEvent ("Mono.runtimeReady", new JObject (), token); SendEvent (sessionId, "Mono.runtimeReady", new JObject (), token);
} }
async Task OnBreakPointHit (JObject args, CancellationToken token) //static int frame_id=0;
async Task OnBreakPointHit (SessionId sessionId, JObject args, CancellationToken token)
{ {
//FIXME we should send release objects every now and then? Or intercept those we inject and deal in the runtime //FIXME we should send release objects every now and then? Or intercept those we inject and deal in the runtime
var o = JObject.FromObject (new { var o = JObject.FromObject (new {
@ -238,11 +256,11 @@ namespace WsProxy {
}); });
var orig_callframes = args? ["callFrames"]?.Values<JObject> (); var orig_callframes = args? ["callFrames"]?.Values<JObject> ();
var res = await SendCommand ("Runtime.evaluate", o, token); var res = await SendCommand (sessionId, "Runtime.evaluate", o, token);
if (res.IsErr) { if (res.IsErr) {
//Give up and send the original call stack //Give up and send the original call stack
SendEvent ("Debugger.paused", args, token); SendEvent (sessionId, "Debugger.paused", args, token);
return; return;
} }
@ -250,16 +268,16 @@ namespace WsProxy {
var res_value = res.Value? ["result"]? ["value"]; var res_value = res.Value? ["result"]? ["value"];
if (res_value == null || res_value is JValue) { if (res_value == null || res_value is JValue) {
//Give up and send the original call stack //Give up and send the original call stack
SendEvent ("Debugger.paused", args, token); SendEvent (sessionId, "Debugger.paused", args, token);
return; return;
} }
Debug ($"call stack (err is {res.Error} value is:\n{res.Value}"); Log ("verbose", $"call stack (err is {res.Error} value is:\n{res.Value}");
var bp_id = res_value? ["breakpoint_id"]?.Value<int> (); var bp_id = res_value? ["breakpoint_id"]?.Value<int> ();
Debug ($"We just hit bp {bp_id}"); Log ("verbose", $"We just hit bp {bp_id}");
if (!bp_id.HasValue) { if (!bp_id.HasValue) {
//Give up and send the original call stack //Give up and send the original call stack
SendEvent ("Debugger.paused", args, token); SendEvent (sessionId, "Debugger.paused", args, token);
return; return;
} }
var bp = this.breakpoints.FirstOrDefault (b => b.RemoteId == bp_id.Value); var bp = this.breakpoints.FirstOrDefault (b => b.RemoteId == bp_id.Value);
@ -282,14 +300,14 @@ namespace WsProxy {
var asm = store.GetAssemblyByName (assembly_name); var asm = store.GetAssemblyByName (assembly_name);
if (asm == null) { if (asm == null) {
Info ($"Unable to find assembly: {assembly_name}"); Log ("info",$"Unable to find assembly: {assembly_name}");
continue; continue;
} }
var method = asm.GetMethodByToken (method_token); var method = asm.GetMethodByToken (method_token);
if (method == null) { if (method == null) {
Info ($"Unable to find il offset: {il_pos} in method token: {method_token} assembly name: {assembly_name}"); Log ("info", $"Unable to find il offset: {il_pos} in method token: {method_token} assembly name: {assembly_name}");
continue; continue;
} }
@ -303,8 +321,8 @@ namespace WsProxy {
continue; continue;
} }
Info ($"frame il offset: {il_pos} method token: {method_token} assembly name: {assembly_name}"); Log ("info", $"frame il offset: {il_pos} method token: {method_token} assembly name: {assembly_name}");
Info ($"\tmethod {method.Name} location: {location}"); Log ("info", $"\tmethod {method.Name} location: {location}");
frames.Add (new Frame (method, location, frame_id)); frames.Add (new Frame (method, location, frame_id));
callFrames.Add (JObject.FromObject (new { callFrames.Add (JObject.FromObject (new {
@ -351,12 +369,12 @@ namespace WsProxy {
hitBreakpoints = bp_list, hitBreakpoints = bp_list,
}); });
SendEvent ("Debugger.paused", o, token); SendEvent (sessionId, "Debugger.paused", o, token);
} }
async Task OnDefaultContext (int ctx_id, JObject aux_data, CancellationToken token) async Task OnDefaultContext (MessageId ctx_id, JObject aux_data, CancellationToken token)
{ {
Debug ("Default context created, clearing state and sending events"); Log ("verbose", "Default context created, clearing state and sending events");
//reset all bps //reset all bps
foreach (var b in this.breakpoints){ foreach (var b in this.breakpoints){
@ -371,16 +389,16 @@ namespace WsProxy {
silent = false, silent = false,
returnByValue = true returnByValue = true
}); });
this.ctx_id = ctx_id; this.ctx_id = ctx_id.id;
this.aux_ctx_data = aux_data; this.aux_ctx_data = aux_data;
Debug ("checking if the runtime is ready"); Log ("verbose", "checking if the runtime is ready");
var res = await SendCommand ("Runtime.evaluate", o, token); var res = await SendCommand (ctx_id, "Runtime.evaluate", o, token);
var is_ready = res.Value? ["result"]? ["value"]?.Value<bool> (); var is_ready = res.Value? ["result"]? ["value"]?.Value<bool> ();
//Debug ($"\t{is_ready}"); //Log ("verbose", $"\t{is_ready}");
if (is_ready.HasValue && is_ready.Value == true) { if (is_ready.HasValue && is_ready.Value == true) {
Debug ("RUNTIME LOOK READY. GO TIME!"); Log ("verbose", "RUNTIME LOOK READY. GO TIME!");
await OnRuntimeReady (token); await OnRuntimeReady (ctx_id, token);
} }
} }
@ -392,7 +410,7 @@ namespace WsProxy {
await Task.CompletedTask; await Task.CompletedTask;
} }
async Task Step (int msg_id, StepKind kind, CancellationToken token) async Task Step (MessageId msg_id, StepKind kind, CancellationToken token)
{ {
var o = JObject.FromObject (new { var o = JObject.FromObject (new {
@ -403,16 +421,16 @@ namespace WsProxy {
returnByValue = true, returnByValue = true,
}); });
var res = await SendCommand ("Runtime.evaluate", o, token); var res = await SendCommand (msg_id, "Runtime.evaluate", o, token);
SendResponse (msg_id, Result.Ok (new JObject ()), token); SendResponse (msg_id, Result.Ok (new JObject ()), token);
this.current_callstack = null; this.current_callstack = null;
await SendCommand ("Debugger.resume", new JObject (), token); await SendCommand (msg_id, "Debugger.resume", new JObject (), token);
} }
async Task GetDetails(int msg_id, int object_id, CancellationToken token, string command) async Task GetDetails(MessageId msg_id, int object_id, CancellationToken token, string command)
{ {
var o = JObject.FromObject(new var o = JObject.FromObject(new
{ {
@ -423,7 +441,7 @@ namespace WsProxy {
returnByValue = true, returnByValue = true,
}); });
var res = await SendCommand("Runtime.evaluate", o, token); var res = await SendCommand(msg_id, "Runtime.evaluate", o, token);
//if we fail we just buble that to the IDE (and let it panic over it) //if we fail we just buble that to the IDE (and let it panic over it)
if (res.IsErr) if (res.IsErr)
@ -461,14 +479,13 @@ namespace WsProxy {
{ {
result = var_list result = var_list
}); });
} catch (Exception) { } catch (Exception e) {
Debug ($"failed to parse {res.Value}"); Log ("verbose", $"failed to parse {res.Value} - {e.Message}");
} }
SendResponse(msg_id, Result.Ok(o), token); SendResponse(msg_id, Result.Ok(o), token);
} }
async Task GetScopeProperties (MessageId msg_id, int scope_id, CancellationToken token)
async Task GetScopeProperties (int msg_id, int scope_id, CancellationToken token)
{ {
var scope = this.current_callstack.FirstOrDefault (s => s.Id == scope_id); var scope = this.current_callstack.FirstOrDefault (s => s.Id == scope_id);
var vars = scope.Method.GetLiveVarsAt (scope.Location.CliLocation.Offset); var vars = scope.Method.GetLiveVarsAt (scope.Location.CliLocation.Offset);
@ -484,7 +501,7 @@ namespace WsProxy {
returnByValue = true, returnByValue = true,
}); });
var res = await SendCommand ("Runtime.evaluate", o, token); var res = await SendCommand (msg_id, "Runtime.evaluate", o, token);
//if we fail we just buble that to the IDE (and let it panic over it) //if we fail we just buble that to the IDE (and let it panic over it)
if (res.IsErr) { if (res.IsErr) {
@ -533,13 +550,13 @@ namespace WsProxy {
result = var_list result = var_list
}); });
SendResponse (msg_id, Result.Ok (o), token); SendResponse (msg_id, Result.Ok (o), token);
} } catch (Exception exception) {
catch (Exception) { Log ("verbose", $"Error resolving scope properties {exception.Message}");
SendResponse (msg_id, res, token); SendResponse (msg_id, res, token);
} }
} }
async Task<Result> EnableBreakPoint (Breakpoint bp, CancellationToken token) async Task<Result> EnableBreakPoint (SessionId sessionId, Breakpoint bp, CancellationToken token)
{ {
var asm_name = bp.Location.CliLocation.Method.Assembly.Name; var asm_name = bp.Location.CliLocation.Method.Assembly.Name;
var method_token = bp.Location.CliLocation.Method.Token; var method_token = bp.Location.CliLocation.Method.Token;
@ -553,21 +570,20 @@ namespace WsProxy {
returnByValue = true, returnByValue = true,
}); });
var res = await SendCommand ("Runtime.evaluate", o, token); var res = await SendCommand (sessionId, "Runtime.evaluate", o, token);
var ret_code = res.Value? ["result"]? ["value"]?.Value<int> (); var ret_code = res.Value? ["result"]? ["value"]?.Value<int> ();
if (ret_code.HasValue) { if (ret_code.HasValue) {
bp.RemoteId = ret_code.Value; bp.RemoteId = ret_code.Value;
bp.State = BreakPointState.Active; bp.State = BreakPointState.Active;
//Debug ($"BP local id {bp.LocalId} enabled with remote id {bp.RemoteId}"); //Log ("verbose", $"BP local id {bp.LocalId} enabled with remote id {bp.RemoteId}");
} }
return res; return res;
} }
async Task RuntimeReady (CancellationToken token) async Task LoadStore (SessionId sessionId, CancellationToken token)
{ {
var o = JObject.FromObject (new { var o = JObject.FromObject (new {
expression = MonoCommands.GET_LOADED_FILES, expression = MonoCommands.GET_LOADED_FILES,
objectGroup = "mono_debugger", objectGroup = "mono_debugger",
@ -575,10 +591,19 @@ namespace WsProxy {
silent = false, silent = false,
returnByValue = true, returnByValue = true,
}); });
var loaded_pdbs = await SendCommand ("Runtime.evaluate", o, token);
var loaded_pdbs = await SendCommand (sessionId, "Runtime.evaluate", o, token);
var the_value = loaded_pdbs.Value? ["result"]? ["value"]; var the_value = loaded_pdbs.Value? ["result"]? ["value"];
var the_pdbs = the_value?.ToObject<string[]> (); var the_pdbs = the_value?.ToObject<string[]> ();
this.store = new DebugStore (the_pdbs);
store = new DebugStore ();
await store.Load(sessionId, the_pdbs, token);
}
async Task RuntimeReady (SessionId sessionId, CancellationToken token)
{
if (store == null)
await LoadStore (sessionId, token);
foreach (var s in store.AllSources ()) { foreach (var s in store.AllSources ()) {
var ok = JObject.FromObject (new { var ok = JObject.FromObject (new {
@ -587,13 +612,13 @@ namespace WsProxy {
executionContextId = this.ctx_id, executionContextId = this.ctx_id,
hash = s.DocHashCode, hash = s.DocHashCode,
executionContextAuxData = this.aux_ctx_data, executionContextAuxData = this.aux_ctx_data,
dotNetUrl = s.DotNetUrl dotNetUrl = s.DotNetUrl,
}); });
//Debug ($"\tsending {s.Url}"); //Log ("verbose", $"\tsending {s.Url}");
SendEvent ("Debugger.scriptParsed", ok, token); SendEvent (sessionId, "Debugger.scriptParsed", ok, token);
} }
o = JObject.FromObject (new { var o = JObject.FromObject (new {
expression = MonoCommands.CLEAR_ALL_BREAKPOINTS, expression = MonoCommands.CLEAR_ALL_BREAKPOINTS,
objectGroup = "mono_debugger", objectGroup = "mono_debugger",
includeCommandLineAPI = false, includeCommandLineAPI = false,
@ -601,9 +626,9 @@ namespace WsProxy {
returnByValue = true, returnByValue = true,
}); });
var clear_result = await SendCommand ("Runtime.evaluate", o, token); var clear_result = await SendCommand (sessionId, "Runtime.evaluate", o, token);
if (clear_result.IsErr) { if (clear_result.IsErr) {
Debug ($"Failed to clear breakpoints due to {clear_result}"); Log ("verbose", $"Failed to clear breakpoints due to {clear_result}");
} }
@ -612,19 +637,19 @@ namespace WsProxy {
foreach (var bp in breakpoints) { foreach (var bp in breakpoints) {
if (bp.State != BreakPointState.Pending) if (bp.State != BreakPointState.Pending)
continue; continue;
var res = await EnableBreakPoint (bp, token); var res = await EnableBreakPoint (sessionId, bp, token);
var ret_code = res.Value? ["result"]? ["value"]?.Value<int> (); 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 we fail we just buble that to the IDE (and let it panic over it)
if (!ret_code.HasValue) { if (!ret_code.HasValue) {
//FIXME figure out how to inform the IDE of that. //FIXME figure out how to inform the IDE of that.
Info ($"FAILED TO ENABLE BP {bp.LocalId}"); Log ("info", $"FAILED TO ENABLE BP {bp.LocalId}");
bp.State = BreakPointState.Disabled; bp.State = BreakPointState.Disabled;
} }
} }
} }
async Task<bool> RemoveBreakpoint(int msg_id, JObject args, CancellationToken token) { async Task<bool> RemoveBreakpoint(MessageId msg_id, JObject args, CancellationToken token) {
var bpid = args? ["breakpointId"]?.Value<string> (); var bpid = args? ["breakpointId"]?.Value<string> ();
if (bpid?.StartsWith ("dotnet:") != true) if (bpid?.StartsWith ("dotnet:") != true)
return false; return false;
@ -633,19 +658,19 @@ namespace WsProxy {
var bp = breakpoints.FirstOrDefault (b => b.LocalId == the_id); var bp = breakpoints.FirstOrDefault (b => b.LocalId == the_id);
if (bp == null) { if (bp == null) {
Info ($"Could not find dotnet bp with id {the_id}"); Log ("info", $"Could not find dotnet bp with id {the_id}");
return false; return false;
} }
breakpoints.Remove (bp); breakpoints.Remove (bp);
//FIXME verify result (and log?) //FIXME verify result (and log?)
var res = await RemoveBreakPoint (bp, token); var res = await RemoveBreakPoint (msg_id, bp, token);
return true; return true;
} }
async Task<Result> RemoveBreakPoint (Breakpoint bp, CancellationToken token) async Task<Result> RemoveBreakPoint (SessionId sessionId, Breakpoint bp, CancellationToken token)
{ {
var o = JObject.FromObject (new { var o = JObject.FromObject (new {
expression = string.Format (MonoCommands.REMOVE_BREAK_POINT, bp.RemoteId), expression = string.Format (MonoCommands.REMOVE_BREAK_POINT, bp.RemoteId),
@ -655,7 +680,7 @@ namespace WsProxy {
returnByValue = true, returnByValue = true,
}); });
var res = await SendCommand ("Runtime.evaluate", o, token); var res = await SendCommand (sessionId, "Runtime.evaluate", o, token);
var ret_code = res.Value? ["result"]? ["value"]?.Value<int> (); var ret_code = res.Value? ["result"]? ["value"]?.Value<int> ();
if (ret_code.HasValue) { if (ret_code.HasValue) {
@ -666,13 +691,13 @@ namespace WsProxy {
return res; return res;
} }
async Task SetBreakPoint (int msg_id, BreakPointRequest req, CancellationToken token) async Task SetBreakPoint (MessageId msg_id, BreakPointRequest req, CancellationToken token)
{ {
var bp_loc = store.FindBestBreakpoint (req); var bp_loc = store?.FindBestBreakpoint (req);
Info ($"BP request for '{req}' runtime ready {runtime_ready} location '{bp_loc}'"); Log ("info", $"BP request for '{req}' runtime ready {runtime_ready} location '{bp_loc}'");
if (bp_loc == null) { if (bp_loc == null) {
Info ($"Could not resolve breakpoint request: {req}"); Log ("info", $"Could not resolve breakpoint request: {req}");
SendResponse (msg_id, Result.Err(JObject.FromObject (new { SendResponse (msg_id, Result.Err(JObject.FromObject (new {
code = (int)MonoErrorCodes.BpNotFound, code = (int)MonoErrorCodes.BpNotFound,
message = $"C# Breakpoint at {req} not found." message = $"C# Breakpoint at {req} not found."
@ -686,7 +711,7 @@ namespace WsProxy {
} else { } else {
bp = new Breakpoint (bp_loc, local_breakpoint_id++, BreakPointState.Disabled); bp = new Breakpoint (bp_loc, local_breakpoint_id++, BreakPointState.Disabled);
var res = await EnableBreakPoint (bp, token); var res = await EnableBreakPoint (msg_id, bp, token);
var ret_code = res.Value? ["result"]? ["value"]?.Value<int> (); 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 we fail we just buble that to the IDE (and let it panic over it)
@ -714,7 +739,7 @@ namespace WsProxy {
SendResponse (msg_id, Result.Ok (ok), token); SendResponse (msg_id, Result.Ok (ok), token);
} }
bool GetPossibleBreakpoints (int msg_id, SourceLocation start, SourceLocation end, CancellationToken token) bool GetPossibleBreakpoints (MessageId msg_id, SourceLocation start, SourceLocation end, CancellationToken token)
{ {
var bps = store.FindPossibleBreakpoints (start, end); var bps = store.FindPossibleBreakpoints (start, end);
if (bps == null) if (bps == null)
@ -734,15 +759,14 @@ namespace WsProxy {
return true; return true;
} }
void OnCompileDotnetScript (int msg_id, CancellationToken token) void OnCompileDotnetScript (MessageId msg_id, CancellationToken token)
{ {
var o = JObject.FromObject (new { }); var o = JObject.FromObject (new { });
SendResponse (msg_id, Result.Ok (o), token); SendResponse (msg_id, Result.Ok (o), token);
} }
async Task OnGetScriptSource (int msg_id, string script_id, CancellationToken token) async Task OnGetScriptSource (MessageId msg_id, string script_id, CancellationToken token)
{ {
var id = new SourceId (script_id); var id = new SourceId (script_id);
var src_file = store.GetFileById (id); var src_file = store.GetFileById (id);
@ -753,6 +777,16 @@ namespace WsProxy {
try { try {
var uri = new Uri (src_file.Url); var uri = new Uri (src_file.Url);
if (uri.IsFile && File.Exists(uri.LocalPath)) { if (uri.IsFile && File.Exists(uri.LocalPath)) {
using (var f = new StreamReader (File.Open (uri.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.SourceUri.IsFile && File.Exists(src_file.SourceUri.LocalPath)) {
using (var f = new StreamReader (File.Open (src_file.SourceUri.LocalPath, FileMode.Open))) { using (var f = new StreamReader (File.Open (src_file.SourceUri.LocalPath, FileMode.Open))) {
await res.WriteAsync (await f.ReadToEndAsync ()); await res.WriteAsync (await f.ReadToEndAsync ());
} }
@ -789,4 +823,4 @@ namespace WsProxy {
} }
} }
} }
} }