diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index db2f14248f68330f38618bf7d502699c1d40c6cf..a2d59b873da0dedb407775967c567da88a5cf8cd 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -57,235 +57,249 @@ <Uri>https://github.com/dotnet/efcore</Uri> <Sha>7ae89d6adb7433b6ae9c37091e57a1226092ef46</Sha> </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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </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.CommandLineUtils.Sources" Version="3.1.2-servicing.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore"> + <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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> + <Dependency Name="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.2" Pinned="true"> <Uri>https://github.com/dotnet/extensions</Uri> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.Configuration.AzureKeyVault" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.Configuration.Binder" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.Configuration.CommandLine" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.Configuration.EnvironmentVariables" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.Configuration.FileExtensions" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.Configuration.Ini" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.Configuration.Json" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.Configuration.KeyPerFile" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.Configuration.UserSecrets" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.Configuration.Xml" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.Configuration" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.DependencyInjection.Abstractions" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.DependencyInjection" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.DiagnosticAdapter" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.Diagnostics.HealthChecks" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.FileProviders.Abstractions" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.FileProviders.Composite" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.FileProviders.Embedded" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.FileProviders.Physical" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.FileSystemGlobbing" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.HashCodeCombiner.Sources" Version="3.1.2-servicing.20067.6" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> + <Dependency Name="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.2" Pinned="true"> <Uri>https://github.com/dotnet/extensions</Uri> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.Hosting" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.HostFactoryResolver.Sources" Version="3.1.2-servicing.20067.6" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.Http" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> + <Dependency Name="Microsoft.Extensions.Http" Version="3.1.2" Pinned="true"> <Uri>https://github.com/dotnet/extensions</Uri> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.Localization.Abstractions" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.Localization" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.Logging.Abstractions" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.Logging.AzureAppServices" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.Logging.Configuration" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.Logging.Console" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.Logging.Debug" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.Logging.EventSource" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.Logging.EventLog" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.Logging.TraceSource" 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> - <Dependency Name="Microsoft.Extensions.Logging.Testing" Version="3.1.2-servicing.20067.6" CoherentParentDependency="Microsoft.EntityFrameworkCore"> + <Dependency Name="Microsoft.Extensions.Logging.Testing" Version="3.1.2-servicing.20067.6" Pinned="true"> <Uri>https://github.com/dotnet/extensions</Uri> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.Logging" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.ObjectPool" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> + <Dependency Name="Microsoft.Extensions.ObjectPool" Version="3.1.2" Pinned="true"> <Uri>https://github.com/dotnet/extensions</Uri> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.Options.ConfigurationExtensions" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.Options.DataAnnotations" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.Options" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.ParameterDefaultValue.Sources" Version="3.1.2-servicing.20067.6" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.Primitives" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.TypeNameHelper.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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.ValueStopwatch.Sources" Version="3.1.2-servicing.20067.6" 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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.Extensions.WebEncoders" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> + <Dependency Name="Microsoft.Extensions.WebEncoders" Version="3.1.2" Pinned="true"> <Uri>https://github.com/dotnet/extensions</Uri> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> - <Dependency Name="Microsoft.JSInterop" Version="3.1.2" CoherentParentDependency="Microsoft.EntityFrameworkCore"> + <Dependency Name="Microsoft.JSInterop" Version="3.1.2" Pinned="true"> <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"> + <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> @@ -379,7 +393,7 @@ </Dependency> <Dependency Name="Microsoft.Extensions.DependencyModel" Version="3.1.2" CoherentParentDependency="Microsoft.Extensions.Logging"> <Uri>https://github.com/dotnet/core-setup</Uri> - <Sha>916b5cba268e1e1e803243004f4276cf40b2dda8</Sha> + <Sha>65f04fb6db7a5e198d05dbebd5c4ad21eb018f89</Sha> </Dependency> <!-- 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> <Sha>0f7f38c4fd323b26da10cce95f857f77f0f09b48</Sha> </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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> @@ -429,7 +443,7 @@ <Uri>https://github.com/dotnet/arcade</Uri> <Sha>4d80b9cfa53e309c8f685abff3512f60c3d8a3d1</Sha> </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> <Sha>1286a6ff55e300352dabeb6d778c9fcdd258bd08</Sha> </Dependency> diff --git a/src/Components/Blazor/Server/src/MonoDebugProxy/BlazorMonoDebugProxyAppBuilderExtensions.cs b/src/Components/Blazor/Server/src/MonoDebugProxy/BlazorMonoDebugProxyAppBuilderExtensions.cs index cbe0fe363a67ce3870dd42c6ba0fc3fb9968b164..394ccbe7187197361c4ee680d1cdd87637ba6b03 100644 --- a/src/Components/Blazor/Server/src/MonoDebugProxy/BlazorMonoDebugProxyAppBuilderExtensions.cs +++ b/src/Components/Blazor/Server/src/MonoDebugProxy/BlazorMonoDebugProxyAppBuilderExtensions.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; using System.Net; @@ -12,7 +11,9 @@ using System.Runtime.InteropServices; using System.Text.Json; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; -using WsProxy; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using WebAssembly.Net.Debugging; namespace Microsoft.AspNetCore.Builder { @@ -50,7 +51,8 @@ namespace Microsoft.AspNetCore.Builder 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)) @@ -111,7 +113,8 @@ namespace Microsoft.AspNetCore.Builder var proxiedTabInfos = availableTabs.Select(tab => { 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 { description = "", @@ -129,10 +132,42 @@ namespace Microsoft.AspNetCore.Builder } 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"; - await context.Response.WriteAsync(browserVersionJson); + await JsonSerializer.SerializeAsync(context.Response.Body, browserVersion); } } 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) { @@ -152,7 +187,7 @@ namespace Microsoft.AspNetCore.Builder var browserUri = new Uri(context.Request.Query["browser"]); 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) @@ -235,13 +270,30 @@ namespace Microsoft.AspNetCore.Builder // page and redirect there var tabToDebug = matchingTabs.Single(); 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 wsParamName = request.IsHttps ? "wss" : "ws"; - var devToolsUrlWithProxy = $"{devToolsUrlAbsolute.Scheme}://{devToolsUrlAbsolute.Authority}{devToolsUrlAbsolute.AbsolutePath}?{wsParamName}={proxyEndpoint}"; + var devToolsUrlWithProxy = $"{devToolsUrlAbsolute.Scheme}://{devToolsUrlAbsolute.Authority}{devToolsUrlAbsolute.AbsolutePath}?{proxyEndpoint.Scheme}={proxyEndpoint.Authority}{proxyEndpoint.PathAndQuery}"; 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) { 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) }; 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() diff --git a/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/DebugStore.cs b/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/DebugStore.cs index e1e9b7392ba854634cce785eb6fe9e7329a5af04..5c46bc7324a37effff160f2910c1074ffa5a6ab0 100644 --- a/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/DebugStore.cs +++ b/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/DebugStore.cs @@ -9,8 +9,10 @@ using System.Net.Http; using Mono.Cecil.Pdb; using Newtonsoft.Json; using System.Text.RegularExpressions; +using System.Threading.Tasks; +using System.Threading; -namespace WsProxy { +namespace WebAssembly.Net.Debugging { internal class BreakPointRequest { public string Assembly { get; private set; } public string File { get; private set; } @@ -23,13 +25,15 @@ namespace WsProxy { 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; var url = args? ["url"]?.Value<string> (); if (url == null) { var urlRegex = args?["urlRegex"].Value<string>(); - var sourceFile = store.GetFileByUrlRegex (urlRegex); + var sourceFile = store?.GetFileByUrlRegex (urlRegex); url = sourceFile?.DotNetUrl; } @@ -72,7 +76,6 @@ namespace WsProxy { } } - internal class VarInfo { public VarInfo (VariableDebugInformation v) { @@ -85,10 +88,10 @@ namespace WsProxy { this.Name = p.Name; this.Index = (p.Index + 1) * -1; } + public string Name { get; private set; } public int Index { get; private set; } - public override string ToString () { return $"(var-info [{Index}] '{Name}')"; @@ -97,18 +100,14 @@ namespace WsProxy { internal class CliLocation { - - private MethodInfo method; - private int offset; - public CliLocation (MethodInfo method, int offset) { - this.method = method; - this.offset = offset; + Method = method; + Offset = offset; } - public MethodInfo Method { get => method; } - public int Offset { get => offset; } + public MethodInfo Method { get; private set; } + public int Offset { get; private set; } } @@ -282,7 +281,6 @@ namespace WsProxy { .Where (v => !v.IsDebuggerHidden) .Select (v => new VarInfo (v))); - return res.ToArray (); } } @@ -294,20 +292,21 @@ namespace WsProxy { Dictionary<int, MethodInfo> methods = new Dictionary<int, MethodInfo> (); Dictionary<string, string> sourceLinkMappings = new Dictionary<string, string>(); 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)) { this.id = ++next_id; } try { + Url = url; ReaderParameters rp = new ReaderParameters (/*ReadingMode.Immediate*/); - if (pdb != null) { - rp.ReadSymbols = true; - rp.SymbolReaderProvider = new PortablePdbReaderProvider (); + rp.ReadSymbols = true; + rp.SymbolReaderProvider = new PdbReaderProvider (); + if (pdb != null) rp.SymbolStream = new MemoryStream (pdb); - } rp.ReadingMode = ReadingMode.Immediate; rp.InMemory = true; @@ -315,13 +314,16 @@ namespace WsProxy { this.image = ModuleDefinition.ReadModule (new MemoryStream (assembly), rp); } catch (BadImageFormatException ex) { Console.WriteLine ($"Failed to read assembly as portable PDB: {ex.Message}"); + } catch (ArgumentNullException) { + if (pdb != null) + throw; } if (this.image == null) { ReaderParameters rp = new ReaderParameters (/*ReadingMode.Immediate*/); if (pdb != null) { rp.ReadSymbols = true; - rp.SymbolReaderProvider = new NativePdbReaderProvider (); + rp.SymbolReaderProvider = new PdbReaderProvider (); rp.SymbolStream = new MemoryStream (pdb); } @@ -491,61 +493,72 @@ namespace WsProxy { } internal class DebugStore { + // MonoProxy proxy; - commenting out because never gets assigned List<AssemblyInfo> assemblies = new List<AssemblyInfo> (); + HttpClient client = new HttpClient (); + + class DebugItem { + public string Url { get; set; } + public Task<byte[][]> Data { get; set; } + } - public DebugStore (string [] loaded_files) + public async Task Load (SessionId sessionId, string [] loaded_files, CancellationToken token) { - bool MatchPdb (string asm, string pdb) - { - return Path.ChangeExtension (asm, "pdb") == pdb; - } + static bool MatchPdb (string asm, string pdb) + => 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; + foreach (var file_name in loaded_files) { if (file_name.EndsWith (".pdb", StringComparison.OrdinalIgnoreCase)) pdb_files.Add (file_name); else asm_files.Add (file_name); } - //FIXME make this parallel - foreach (var p in asm_files) { + List<DebugItem> steps = new List<DebugItem> (); + foreach (var url in asm_files) { 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)); + var pdb = pdb_files.FirstOrDefault (n => MatchPdb (url, n)); + steps.Add ( + new DebugItem { + Url = url, + Data = Task.WhenAll (client.GetByteArrayAsync (url), pdb != null ? client.GetByteArrayAsync (pdb) : Task.FromResult<byte []> (null)) + }); } 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 + } } - } - public IEnumerable<SourceFile> AllSources () - { - foreach (var a in assemblies) { - foreach (var s in a.Sources) - yield return s; + 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 () + => assemblies.SelectMany (a => a.Sources); + public SourceFile GetFileById (SourceId id) - { - return AllSources ().FirstOrDefault (f => f.SourceId.Equals (id)); - } + => AllSources ().FirstOrDefault (f => f.SourceId.Equals (id)); public AssemblyInfo GetAssemblyByName (string name) - { - return assemblies.FirstOrDefault (a => a.Name.Equals (name, StringComparison.InvariantCultureIgnoreCase)); - } + => assemblies.FirstOrDefault (a => a.Name.Equals (name, StringComparison.InvariantCultureIgnoreCase)); - /* + /* V8 uses zero based indexing for both line and column. PPDBs uses one based indexing for both line and column. */ @@ -598,7 +611,7 @@ namespace WsProxy { PPDBs uses one based indexing for both line and column. */ static bool Match (SequencePoint sp, int line, int column) - { + { var bp = (line: line + 1, column: column + 1); if (sp.StartLine > bp.line || sp.EndLine < bp.line) diff --git a/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/DevToolsClient.cs b/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/DevToolsClient.cs new file mode 100644 index 0000000000000000000000000000000000000000..3fa95ef3463a6d809ac56c5dfb3f30178f3cede4 --- /dev/null +++ b/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/DevToolsClient.cs @@ -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) + { + // + } + } +} diff --git a/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/WsProxy.cs b/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/DevToolsProxy.cs similarity index 57% rename from src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/WsProxy.cs rename to src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/DevToolsProxy.cs index 87ef23027e8a0fd86535aacf16bfd5708b696321..e2e24eff79429158e0d25d045e4fea2a692dfbc6 100644 --- a/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/WsProxy.cs +++ b/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/DevToolsProxy.cs @@ -8,8 +8,16 @@ using System.Threading; using System.IO; using System.Text; 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 { public JObject Value { get; private set; } @@ -26,6 +34,7 @@ namespace WsProxy { public static Result FromJson (JObject obj) { + //Log ("protocol", $"from result: {obj}"); return new Result (obj ["result"] as JObject, obj ["error"] as JObject); } @@ -39,28 +48,30 @@ namespace WsProxy { return new Result (null, err); } - public JObject ToJObject (int id) { + public JObject ToJObject (MessageId target) { if (IsOk) { return JObject.FromObject (new { - id = id, + target.id, + target.sessionId, result = Value }); } else { return JObject.FromObject (new { - id = id, + target.id, + target.sessionId, error = Error }); } } } - class WsQueue { + class DevToolsQueue { Task current_send; List<byte []> pending; public WebSocket Ws { get; private set; } public Task CurrentSend { get { return current_send; } } - public WsQueue (WebSocket sock) + public DevToolsQueue (WebSocket sock) { this.Ws = sock; pending = new List<byte []> (); @@ -71,7 +82,7 @@ namespace WsProxy { pending.Add (bytes); if (pending.Count == 1) { 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); current_send = Ws.SendAsync (new ArraySegment<byte> (bytes), WebSocketMessageType.Text, true, token); return current_send; @@ -86,7 +97,7 @@ namespace WsProxy { if (pending.Count > 0) { 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); current_send = Ws.SendAsync (new ArraySegment<byte> (pending [0]), WebSocketMessageType.Text, true, token); return current_send; @@ -95,22 +106,28 @@ namespace WsProxy { } } - internal class WsProxy { + internal class DevToolsProxy { TaskCompletionSource<bool> side_exception = 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; WebSocket ide; int next_cmd_id; List<Task> pending_ops = new List<Task> (); - List<WsQueue> queues = new List<WsQueue> (); + List<DevToolsQueue> queues = new List<DevToolsQueue> (); + readonly ILogger logger; + + public DevToolsProxy(ILoggerFactory loggerFactory) + { + logger = loggerFactory.CreateLogger<DevToolsProxy>(); + } - protected virtual Task<bool> AcceptEvent (string method, JObject args, CancellationToken token) + protected virtual Task<bool> AcceptEvent (SessionId sessionId, string method, JObject args, CancellationToken token) { 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); } @@ -122,7 +139,7 @@ namespace WsProxy { while (true) { 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); return null; } @@ -133,51 +150,53 @@ namespace WsProxy { 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); - } else { - mem.Write (buff, 0, result.Count); - } } } - WsQueue GetQueueForSocket (WebSocket ws) + DevToolsQueue GetQueueForSocket (WebSocket ws) { return queues.FirstOrDefault (q => q.Ws == ws); } - WsQueue GetQueueForTask (Task task) { + DevToolsQueue GetQueueForTask (Task task) + { return queues.FirstOrDefault (q => q.CurrentSend == task); } 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 task = queue.Send (bytes, token); if (task != null) 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 { - if (!await AcceptEvent (method, args, token)) { + if (!await AcceptEvent (sessionId, method, args, token)) { //Console.WriteLine ("proxy browser: {0}::{1}",method, args); - SendEventInternal (method, args, token); + SendEventInternal (sessionId, method, args, token); } } catch (Exception 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 { 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); } } 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); - 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]; pending_cmds.RemoveAt (idx); @@ -197,68 +217,74 @@ namespace WsProxy { void ProcessBrowserMessage (string msg, CancellationToken token) { - // Debug ($"browser: {msg}"); + Log ("protocol", $"browser: {msg}"); var res = JObject.Parse (msg); 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 - 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) { + Log ("protocol", $"ide: {msg}"); if (!string.IsNullOrEmpty (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) { - // Debug ($"sending command {method}: {args}"); - return await SendCommandInternal (method, args, token); + internal async Task<Result> SendCommand (SessionId id, string method, JObject args, CancellationToken token) { + //Log ("verbose", $"sending command {method}: {args}"); + 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; var o = JObject.FromObject (new { - id = id, - method = method, + sessionId.sessionId, + id, + method, @params = args }); 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); 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}"); - SendEventInternal (method, args, token); + //Log ("verbose", $"sending event {method}: {args}"); + 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 { - method = method, + sessionId.sessionId, + method, @params = args }); 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); } - void SendResponseInternal (int id, Result result, CancellationToken token) + void SendResponseInternal (MessageId id, Result result, CancellationToken token) { JObject o = result.ToJObject (id); @@ -268,16 +294,16 @@ namespace WsProxy { // , HttpContext context) public async Task Run (Uri browserUri, WebSocket ideSocket) { - Debug ($"WsProxy Starting on {browserUri}"); + Log ("info", $"DevToolsProxy: Starting on {browserUri}"); using (this.ide = ideSocket) { - Debug ($"WsProxy: IDE waiting for connection on {browserUri}"); - queues.Add (new WsQueue (this.ide)); + Log ("verbose", $"DevToolsProxy: IDE waiting for connection on {browserUri}"); + queues.Add (new DevToolsQueue (this.ide)); using (this.browser = new ClientWebSocket ()) { this.browser.Options.KeepAliveInterval = Timeout.InfiniteTimeSpan; await this.browser.ConnectAsync (browserUri, CancellationToken.None); - queues.Add (new WsQueue (this.browser)); + queues.Add (new DevToolsQueue (this.browser)); - Debug ($"WsProxy: Client connected on {browserUri}"); + Log ("verbose", $"DevToolsProxy: Client connected on {browserUri}"); var x = new CancellationTokenSource (); 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???"); } else if (task == pending_ops [3]) { var res = ((Task<bool>)task).Result; - Debug ($"WsProxy: Client initiated close from {browserUri}"); + Log ("verbose", $"DevToolsProxy: Client initiated close from {browserUri}"); x.Cancel (); } else { //must be a background task @@ -320,7 +346,7 @@ namespace WsProxy { } } } catch (Exception e) { - Debug ($"WsProxy::Run: Exception {e}"); + Log ("error", $"DevToolsProxy::Run: Exception {e}"); //throw; } finally { if (!x.IsCancellationRequested) @@ -330,14 +356,28 @@ namespace WsProxy { } } - protected void Debug (string msg) + protected void Log (string priority, string msg) { - Console.WriteLine (msg); - } - - protected void Info (string msg) - { - Console.WriteLine (msg); + switch (priority) { + case "protocol": + logger.LogTrace (msg); + break; + case "verbose": + 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; + } } } } diff --git a/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/MonoProxy.cs b/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/MonoProxy.cs index eb4cf65b50a41b54e41375e0939b4b104a70ca29..e74d4684948618bb0661a5510a10c20ff809fd92 100644 --- a/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/MonoProxy.cs +++ b/src/Components/Blazor/Server/src/MonoDebugProxy/ws-proxy/MonoProxy.cs @@ -3,14 +3,13 @@ using System.Linq; using System.Threading.Tasks; using Newtonsoft.Json.Linq; -using System.Net.WebSockets; using System.Threading; using System.IO; -using System.Text; using System.Collections.Generic; using System.Net; +using Microsoft.Extensions.Logging; -namespace WsProxy { +namespace WebAssembly.Net.Debugging { internal class MonoCommands { public const string GET_CALL_STACK = "MONO.mono_wasm_get_call_stack()"; @@ -29,10 +28,10 @@ namespace WsProxy { BpNotFound = 100000, } - internal class MonoConstants { public const string RUNTIME_IS_READY = "mono_wasm_runtime_ready"; } + class Frame { public Frame (MethodInfo method, SourceLocation location, int id) { @@ -73,7 +72,7 @@ namespace WsProxy { Over } - internal class MonoProxy : WsProxy { + internal class MonoProxy : DevToolsProxy { DebugStore store; List<Breakpoint> breakpoints = new List<Breakpoint> (); List<Frame> current_callstack; @@ -82,9 +81,9 @@ namespace WsProxy { int ctx_id; 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) { case "Runtime.executionContextCreated": { @@ -93,8 +92,8 @@ namespace WsProxy { if (aux_data != null) { var is_default = aux_data ["isDefault"]?.Value<bool> (); if (is_default == true) { - var ctx_id = ctx ["id"].Value<int> (); - await OnDefaultContext (ctx_id, aux_data, token); + var id = new MessageId { id = ctx ["id"].Value<int> (), sessionId = sessionId.sessionId }; + await OnDefaultContext (id, aux_data, token); } } 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 var top_func = args? ["callFrames"]? [0]? ["functionName"]?.Value<string> (); if (top_func == "mono_wasm_fire_bp" || top_func == "_mono_wasm_fire_bp") { - await OnBreakPointHit (args, token); + await OnBreakPointHit (sessionId, args, token); return true; } if (top_func == MonoConstants.RUNTIME_IS_READY) { - await OnRuntimeReady (token); + await OnRuntimeReady (new SessionId { sessionId = sessionId.sessionId }, token); return true; } break; @@ -119,24 +118,33 @@ namespace WsProxy { } break; } + case "Debugger.enabled": { + await LoadStore (new SessionId { sessionId = args? ["sessionId"]?.Value<string> () }, token); + break; + } } - 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) { + case "Target.attachToTarget": { + break; + } + case "Target.attachToBrowserTarget": { + break; + } case "Debugger.getScriptSource": { var script_id = args? ["scriptId"]?.Value<string> (); if (script_id.StartsWith ("dotnet://", StringComparison.InvariantCultureIgnoreCase)) { await OnGetScriptSource (id, script_id, token); return true; } - break; } + case "Runtime.compileScript": { var exp = args? ["expression"]?.Value<string> (); if (exp.StartsWith ("//dotnet:", StringComparison.InvariantCultureIgnoreCase)) { @@ -156,7 +164,7 @@ namespace WsProxy { } case "Debugger.setBreakpointByUrl": { - Info ($"BP req {args}"); + Log ("info", $"BP req {args}"); var bp_req = BreakPointRequest.Parse (args, store); if (bp_req != null) { await SetBreakPoint (id, bp_req, token); @@ -164,9 +172,10 @@ namespace WsProxy { } break; } + case "Debugger.removeBreakpoint": { - return await RemoveBreakpoint (id, args, token); - } + return await RemoveBreakpoint (id, args, token); + } case "Debugger.resume": { await OnResume (token); @@ -199,16 +208,24 @@ namespace WsProxy { case "Runtime.getProperties": { var objId = args? ["objectId"]?.Value<string> (); - if (objId.StartsWith ("dotnet:scope:", StringComparison.InvariantCulture)) { - 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); + if (objId.StartsWith ("dotnet:")) { + var parts = objId.Split (new char [] { ':' }); + if (parts.Length < 3) + return true; + switch (parts[1]) { + case "scope": { + await GetScopeProperties (id, int.Parse (parts[2]), token); + break; + } + 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; } break; @@ -218,15 +235,16 @@ namespace WsProxy { return false; } - async Task OnRuntimeReady (CancellationToken token) + async Task OnRuntimeReady (SessionId sessionId, CancellationToken token) { - Info ("RUNTIME READY, PARTY TIME"); - await RuntimeReady (token); - await SendCommand ("Debugger.resume", new JObject (), token); - SendEvent ("Mono.runtimeReady", new JObject (), token); + Log ("info", "RUNTIME READY, PARTY TIME"); + await RuntimeReady (sessionId, token); + await SendCommand (sessionId, "Debugger.resume", 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 var o = JObject.FromObject (new { @@ -238,11 +256,11 @@ namespace WsProxy { }); 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) { //Give up and send the original call stack - SendEvent ("Debugger.paused", args, token); + SendEvent (sessionId, "Debugger.paused", args, token); return; } @@ -250,16 +268,16 @@ namespace WsProxy { var res_value = res.Value? ["result"]? ["value"]; if (res_value == null || res_value is JValue) { //Give up and send the original call stack - SendEvent ("Debugger.paused", args, token); + SendEvent (sessionId, "Debugger.paused", args, token); 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> (); - Debug ($"We just hit bp {bp_id}"); + Log ("verbose", $"We just hit bp {bp_id}"); if (!bp_id.HasValue) { //Give up and send the original call stack - SendEvent ("Debugger.paused", args, token); + SendEvent (sessionId, "Debugger.paused", args, token); return; } var bp = this.breakpoints.FirstOrDefault (b => b.RemoteId == bp_id.Value); @@ -282,14 +300,14 @@ namespace WsProxy { var asm = store.GetAssemblyByName (assembly_name); if (asm == null) { - Info ($"Unable to find assembly: {assembly_name}"); + Log ("info",$"Unable to find assembly: {assembly_name}"); continue; } var method = asm.GetMethodByToken (method_token); 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; } @@ -303,8 +321,8 @@ namespace WsProxy { continue; } - Info ($"frame il offset: {il_pos} method token: {method_token} assembly name: {assembly_name}"); - Info ($"\tmethod {method.Name} location: {location}"); + Log ("info", $"frame il offset: {il_pos} method token: {method_token} assembly name: {assembly_name}"); + Log ("info", $"\tmethod {method.Name} location: {location}"); frames.Add (new Frame (method, location, frame_id)); callFrames.Add (JObject.FromObject (new { @@ -351,12 +369,12 @@ namespace WsProxy { 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 foreach (var b in this.breakpoints){ @@ -371,16 +389,16 @@ namespace WsProxy { silent = false, returnByValue = true }); - this.ctx_id = ctx_id; + this.ctx_id = ctx_id.id; this.aux_ctx_data = aux_data; - Debug ("checking if the runtime is ready"); - var res = await SendCommand ("Runtime.evaluate", o, token); + Log ("verbose", "checking if the runtime is ready"); + var res = await SendCommand (ctx_id, "Runtime.evaluate", o, token); 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) { - Debug ("RUNTIME LOOK READY. GO TIME!"); - await OnRuntimeReady (token); + Log ("verbose", "RUNTIME LOOK READY. GO TIME!"); + await OnRuntimeReady (ctx_id, token); } } @@ -392,7 +410,7 @@ namespace WsProxy { 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 { @@ -403,16 +421,16 @@ namespace WsProxy { 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); 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 { @@ -423,7 +441,7 @@ namespace WsProxy { 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 (res.IsErr) @@ -461,14 +479,13 @@ namespace WsProxy { { result = var_list }); - } catch (Exception) { - Debug ($"failed to parse {res.Value}"); + } catch (Exception e) { + Log ("verbose", $"failed to parse {res.Value} - {e.Message}"); } SendResponse(msg_id, Result.Ok(o), token); } - - async Task GetScopeProperties (int msg_id, int scope_id, CancellationToken token) + async Task GetScopeProperties (MessageId msg_id, int scope_id, CancellationToken token) { var scope = this.current_callstack.FirstOrDefault (s => s.Id == scope_id); var vars = scope.Method.GetLiveVarsAt (scope.Location.CliLocation.Offset); @@ -484,7 +501,7 @@ namespace WsProxy { 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 (res.IsErr) { @@ -533,13 +550,13 @@ namespace WsProxy { result = var_list }); SendResponse (msg_id, Result.Ok (o), token); - } - catch (Exception) { + } catch (Exception exception) { + Log ("verbose", $"Error resolving scope properties {exception.Message}"); 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 method_token = bp.Location.CliLocation.Method.Token; @@ -553,21 +570,20 @@ namespace WsProxy { 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> (); if (ret_code.HasValue) { bp.RemoteId = ret_code.Value; 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; } - async Task RuntimeReady (CancellationToken token) + async Task LoadStore (SessionId sessionId, CancellationToken token) { - var o = JObject.FromObject (new { expression = MonoCommands.GET_LOADED_FILES, objectGroup = "mono_debugger", @@ -575,10 +591,19 @@ namespace WsProxy { silent = false, 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_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 ()) { var ok = JObject.FromObject (new { @@ -587,13 +612,13 @@ namespace WsProxy { executionContextId = this.ctx_id, hash = s.DocHashCode, executionContextAuxData = this.aux_ctx_data, - dotNetUrl = s.DotNetUrl + dotNetUrl = s.DotNetUrl, }); - //Debug ($"\tsending {s.Url}"); - SendEvent ("Debugger.scriptParsed", ok, token); + //Log ("verbose", $"\tsending {s.Url}"); + SendEvent (sessionId, "Debugger.scriptParsed", ok, token); } - o = JObject.FromObject (new { + var o = JObject.FromObject (new { expression = MonoCommands.CLEAR_ALL_BREAKPOINTS, objectGroup = "mono_debugger", includeCommandLineAPI = false, @@ -601,9 +626,9 @@ namespace WsProxy { 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) { - 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) { if (bp.State != BreakPointState.Pending) continue; - var res = await EnableBreakPoint (bp, token); + var res = await EnableBreakPoint (sessionId, bp, token); var ret_code = res.Value? ["result"]? ["value"]?.Value<int> (); //if we fail we just buble that to the IDE (and let it panic over it) if (!ret_code.HasValue) { //FIXME figure out how to inform the IDE of that. - Info ($"FAILED TO ENABLE BP {bp.LocalId}"); + Log ("info", $"FAILED TO ENABLE BP {bp.LocalId}"); 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> (); if (bpid?.StartsWith ("dotnet:") != true) return false; @@ -633,19 +658,19 @@ namespace WsProxy { var bp = breakpoints.FirstOrDefault (b => b.LocalId == the_id); 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; } breakpoints.Remove (bp); //FIXME verify result (and log?) - var res = await RemoveBreakPoint (bp, token); + var res = await RemoveBreakPoint (msg_id, bp, token); 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 { expression = string.Format (MonoCommands.REMOVE_BREAK_POINT, bp.RemoteId), @@ -655,7 +680,7 @@ namespace WsProxy { 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> (); if (ret_code.HasValue) { @@ -666,13 +691,13 @@ namespace WsProxy { 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); - Info ($"BP request for '{req}' runtime ready {runtime_ready} location '{bp_loc}'"); + var bp_loc = store?.FindBestBreakpoint (req); + Log ("info", $"BP request for '{req}' runtime ready {runtime_ready} location '{bp_loc}'"); 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 { code = (int)MonoErrorCodes.BpNotFound, message = $"C# Breakpoint at {req} not found." @@ -686,7 +711,7 @@ namespace WsProxy { } else { 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> (); //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); } - 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); if (bps == null) @@ -734,15 +759,14 @@ namespace WsProxy { return true; } - void OnCompileDotnetScript (int msg_id, CancellationToken token) + void OnCompileDotnetScript (MessageId msg_id, CancellationToken token) { var o = JObject.FromObject (new { }); 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 src_file = store.GetFileById (id); @@ -753,6 +777,16 @@ namespace WsProxy { try { var uri = new Uri (src_file.Url); 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))) { await res.WriteAsync (await f.ReadToEndAsync ()); } @@ -789,4 +823,4 @@ namespace WsProxy { } } } -} +} \ No newline at end of file