Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/dotnet/runtime.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/mono/wasm')
-rw-r--r--src/mono/wasm/Makefile2
-rw-r--r--src/mono/wasm/README.md2
-rw-r--r--src/mono/wasm/Wasm.Build.Tests/BrowserRunner.cs10
-rw-r--r--src/mono/wasm/Wasm.Build.Tests/BuildEnvironment.cs5
-rw-r--r--src/mono/wasm/Wasm.Build.Tests/BuildTestBase.cs22
-rw-r--r--src/mono/wasm/Wasm.Build.Tests/DotNetCommand.cs2
-rw-r--r--src/mono/wasm/Wasm.Build.Tests/RunCommand.cs2
-rw-r--r--src/mono/wasm/Wasm.Build.Tests/Wasm.Build.Tests.csproj3
-rw-r--r--src/mono/wasm/Wasm.Build.Tests/WasmTemplateTests.cs62
-rw-r--r--src/mono/wasm/build/WasmApp.targets5
-rw-r--r--src/mono/wasm/debugger/BrowserDebugHost/DebugProxyHost.cs1
-rw-r--r--src/mono/wasm/debugger/BrowserDebugProxy/MemberObjectsExplorer.cs56
-rw-r--r--src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs11
-rw-r--r--src/mono/wasm/debugger/BrowserDebugProxy/ValueTypeClass.cs7
-rw-r--r--src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs9
-rw-r--r--src/mono/wasm/debugger/DebuggerTestSuite/GetPropertiesTests.cs113
-rw-r--r--src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs31
-rw-r--r--src/mono/wasm/debugger/tests/debugger-test-with-non-user-code-class/test.cs4
-rw-r--r--src/mono/wasm/debugger/tests/debugger-test-without-debug-symbols-to-load/test.cs4
-rw-r--r--src/mono/wasm/debugger/tests/debugger-test/debugger-get-properties-test.cs41
-rw-r--r--src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs21
-rw-r--r--src/mono/wasm/host/BrowserHost.cs10
-rw-r--r--src/mono/wasm/host/WebServer.cs33
-rw-r--r--src/mono/wasm/host/WebServerOptions.cs2
-rw-r--r--src/mono/wasm/host/WebServerStartup.cs124
-rw-r--r--src/mono/wasm/runtime/corebindings.c2
-rw-r--r--src/mono/wasm/runtime/driver.c8
-rw-r--r--src/mono/wasm/runtime/logging.ts5
-rw-r--r--src/mono/wasm/runtime/managed-exports.ts21
-rw-r--r--src/mono/wasm/runtime/marshal-to-js.ts8
-rw-r--r--src/mono/wasm/runtime/marshal.ts30
-rw-r--r--src/mono/wasm/runtime/types.ts3
-rw-r--r--src/mono/wasm/templates/templates/browser/.template.config/template.json41
-rw-r--r--src/mono/wasm/templates/templates/browser/Properties/launchSettings.json13
-rw-r--r--src/mono/wasm/templates/templates/browser/main.js11
-rw-r--r--src/mono/wasm/templates/templates/console/.template.config/template.json22
-rw-r--r--src/mono/wasm/templates/templates/console/main.mjs9
-rw-r--r--src/mono/wasm/wasm.proj3
38 files changed, 576 insertions, 182 deletions
diff --git a/src/mono/wasm/Makefile b/src/mono/wasm/Makefile
index 494c7fe365e..9188f97550f 100644
--- a/src/mono/wasm/Makefile
+++ b/src/mono/wasm/Makefile
@@ -140,6 +140,8 @@ build-dbg-proxy:
$(DOTNET) build $(TOP)/src/mono/wasm/debugger/BrowserDebugHost $(MSBUILD_ARGS)
build-dbg-testsuite:
$(DOTNET) build $(TOP)/src/mono/wasm/debugger/DebuggerTestSuite $(MSBUILD_ARGS)
+build-app-host:
+ $(DOTNET) build $(TOP)/src/mono/wasm/host $(_MSBUILD_WASM_BUILD_ARGS) $(MSBUILD_ARGS)
patch-deterministic:
cd emsdk/upstream/emscripten/ && patch -p1 < ../../../runtime/deterministic.diff
diff --git a/src/mono/wasm/README.md b/src/mono/wasm/README.md
index 221fce0e52b..6be63856cd6 100644
--- a/src/mono/wasm/README.md
+++ b/src/mono/wasm/README.md
@@ -177,7 +177,7 @@ Also check [bench](../sample/wasm/browser-bench/README.md) sample to measure mon
## Templates
-The wasm templates, located in the `templates` directory, are templates for `dotnet new`, VS and VS for Mac. They are packaged and distributed as part of the `wasm-tools` workload. We have 2 templates, `wasmbrowser` and `wasmconsole`, for browser and console WebAssembly applications.
+The wasm templates, located in the `templates` directory, are templates for `dotnet new`, VS and VS for Mac. They are packaged and distributed as part of the `wasm-experimental` workload. We have 2 templates, `wasmbrowser` and `wasmconsole`, for browser and console WebAssembly applications.
For details about using `dotnet new` see the dotnet tool [documentation](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-new).
diff --git a/src/mono/wasm/Wasm.Build.Tests/BrowserRunner.cs b/src/mono/wasm/Wasm.Build.Tests/BrowserRunner.cs
index 13a4ff96ab7..718e7f90a84 100644
--- a/src/mono/wasm/Wasm.Build.Tests/BrowserRunner.cs
+++ b/src/mono/wasm/Wasm.Build.Tests/BrowserRunner.cs
@@ -10,6 +10,7 @@ using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.Playwright;
using Wasm.Tests.Internal;
+using Xunit.Abstractions;
namespace Wasm.Build.Tests;
@@ -29,6 +30,9 @@ internal class BrowserRunner : IAsyncDisposable
public Task<CommandResult>? RunTask { get; private set; }
public IList<string> OutputLines { get; private set; } = new List<string>();
private TaskCompletionSource<int> _exited = new();
+ private readonly ITestOutputHelper _testOutput;
+
+ public BrowserRunner(ITestOutputHelper testOutput) => _testOutput = testOutput;
// FIXME: options
public async Task<IPage> RunAsync(ToolCommand cmd, string args, bool headless = true)
@@ -78,7 +82,7 @@ internal class BrowserRunner : IAsyncDisposable
var url = new Uri(urlAvailable.Task.Result);
Playwright = await Microsoft.Playwright.Playwright.CreateAsync();
string[] chromeArgs = new[] { $"--explicitly-allowed-ports={url.Port}" };
- Console.WriteLine($"Launching chrome ('{s_chromePath.Value}') via playwright with args = {string.Join(',', chromeArgs)}");
+ _testOutput.WriteLine($"Launching chrome ('{s_chromePath.Value}') via playwright with args = {string.Join(',', chromeArgs)}");
Browser = await Playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions{
ExecutablePath = s_chromePath.Value,
Headless = headless,
@@ -99,7 +103,7 @@ internal class BrowserRunner : IAsyncDisposable
await Task.WhenAny(RunTask!, _exited.Task, Task.Delay(timeout));
if (_exited.Task.IsCompleted)
{
- Console.WriteLine ($"Exited with {await _exited.Task}");
+ _testOutput.WriteLine ($"Exited with {await _exited.Task}");
return;
}
@@ -114,7 +118,7 @@ internal class BrowserRunner : IAsyncDisposable
await Task.WhenAny(RunTask!, _exited.Task, Task.Delay(timeout));
if (RunTask.IsCanceled)
{
- Console.WriteLine ($"Exited with {(await RunTask).ExitCode}");
+ _testOutput.WriteLine ($"Exited with {(await RunTask).ExitCode}");
return;
}
diff --git a/src/mono/wasm/Wasm.Build.Tests/BuildEnvironment.cs b/src/mono/wasm/Wasm.Build.Tests/BuildEnvironment.cs
index d8ec1e5005b..f921b870375 100644
--- a/src/mono/wasm/Wasm.Build.Tests/BuildEnvironment.cs
+++ b/src/mono/wasm/Wasm.Build.Tests/BuildEnvironment.cs
@@ -27,6 +27,7 @@ namespace Wasm.Build.Tests
public static readonly string RelativeTestAssetsPath = @"..\testassets\";
public static readonly string TestAssetsPath = Path.Combine(AppContext.BaseDirectory, "testassets");
public static readonly string TestDataPath = Path.Combine(AppContext.BaseDirectory, "data");
+ public static readonly string TmpPath = Path.Combine(Path.GetTempPath(), "wasmbuildtests");
private static readonly Dictionary<string, string> s_runtimePackVersions = new();
@@ -141,6 +142,10 @@ namespace Wasm.Build.Tests
{
LogRootPath = Environment.CurrentDirectory;
}
+
+ if (Directory.Exists(TmpPath))
+ Directory.Delete(TmpPath, recursive: true);
+ Directory.CreateDirectory(TmpPath);
}
// FIXME: error checks
diff --git a/src/mono/wasm/Wasm.Build.Tests/BuildTestBase.cs b/src/mono/wasm/Wasm.Build.Tests/BuildTestBase.cs
index e34917b2226..f4a73bdbb0f 100644
--- a/src/mono/wasm/Wasm.Build.Tests/BuildTestBase.cs
+++ b/src/mono/wasm/Wasm.Build.Tests/BuildTestBase.cs
@@ -290,13 +290,25 @@ namespace Wasm.Build.Tests
Directory.CreateDirectory(_logPath);
}
- protected static void InitProjectDir(string dir)
+ protected static void InitProjectDir(string dir, bool addNuGetSourceForLocalPackages = false)
{
Directory.CreateDirectory(dir);
File.WriteAllText(Path.Combine(dir, "Directory.Build.props"), s_buildEnv.DirectoryBuildPropsContents);
File.WriteAllText(Path.Combine(dir, "Directory.Build.targets"), s_buildEnv.DirectoryBuildTargetsContents);
- File.Copy(Path.Combine(BuildEnvironment.TestDataPath, NuGetConfigFileNameForDefaultFramework), Path.Combine(dir, "nuget.config"));
+ string targetNuGetConfigPath = Path.Combine(dir, "nuget.config");
+ if (addNuGetSourceForLocalPackages)
+ {
+ File.WriteAllText(targetNuGetConfigPath,
+ GetNuGetConfigWithLocalPackagesPath(
+ Path.Combine(BuildEnvironment.TestDataPath, NuGetConfigFileNameForDefaultFramework),
+ s_buildEnv.BuiltNuGetsPath));
+ }
+ else
+ {
+ File.Copy(Path.Combine(BuildEnvironment.TestDataPath, NuGetConfigFileNameForDefaultFramework),
+ targetNuGetConfigPath);
+ }
Directory.CreateDirectory(Path.Combine(dir, ".nuget"));
}
@@ -444,10 +456,10 @@ namespace Wasm.Build.Tests
return contents.Replace(s_nugetInsertionTag, $@"<add key=""nuget-local"" value=""{localNuGetsPath}"" />");
}
- public string CreateWasmTemplateProject(string id, string template = "wasmbrowser")
+ public string CreateWasmTemplateProject(string id, string template = "wasmbrowser", string extraArgs = "")
{
InitPaths(id);
- InitProjectDir(id);
+ InitProjectDir(id, addNuGetSourceForLocalPackages: true);
File.WriteAllText(Path.Combine(_projectDir, "Directory.Build.props"), "<Project />");
File.WriteAllText(Path.Combine(_projectDir, "Directory.Build.targets"),
@@ -464,7 +476,7 @@ namespace Wasm.Build.Tests
new DotNetCommand(s_buildEnv, _testOutput, useDefaultArgs: false)
.WithWorkingDirectory(_projectDir!)
- .ExecuteWithCapturedOutput($"new {template}")
+ .ExecuteWithCapturedOutput($"new {template} {extraArgs}")
.EnsureSuccessful();
return Path.Combine(_projectDir!, $"{id}.csproj");
diff --git a/src/mono/wasm/Wasm.Build.Tests/DotNetCommand.cs b/src/mono/wasm/Wasm.Build.Tests/DotNetCommand.cs
index 84ccf2fa23a..44f58f5cc45 100644
--- a/src/mono/wasm/Wasm.Build.Tests/DotNetCommand.cs
+++ b/src/mono/wasm/Wasm.Build.Tests/DotNetCommand.cs
@@ -16,6 +16,8 @@ namespace Wasm.Build.Tests
_useDefaultArgs = useDefaultArgs;
if (useDefaultArgs)
WithEnvironmentVariables(buildEnv.EnvVars);
+ // workaround msbuild issue - https://github.com/dotnet/runtime/issues/74328
+ WithEnvironmentVariable("DOTNET_CLI_DO_NOT_USE_MSBUILD_SERVER", "1");
}
protected override string GetFullArgs(params string[] args)
diff --git a/src/mono/wasm/Wasm.Build.Tests/RunCommand.cs b/src/mono/wasm/Wasm.Build.Tests/RunCommand.cs
index 01f1e1efacd..33759316d1e 100644
--- a/src/mono/wasm/Wasm.Build.Tests/RunCommand.cs
+++ b/src/mono/wasm/Wasm.Build.Tests/RunCommand.cs
@@ -14,5 +14,7 @@ public class RunCommand : DotNetCommand
WithEnvironmentVariable("DOTNET_INSTALL_DIR", Path.GetDirectoryName(buildEnv.DotNet)!);
WithEnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0");
WithEnvironmentVariable("DOTNET_SKIP_FIRST_TIME_EXPERIENCE", "1");
+ // workaround msbuild issue - https://github.com/dotnet/runtime/issues/74328
+ WithEnvironmentVariable("DOTNET_CLI_DO_NOT_USE_MSBUILD_SERVER", "1");
}
}
diff --git a/src/mono/wasm/Wasm.Build.Tests/Wasm.Build.Tests.csproj b/src/mono/wasm/Wasm.Build.Tests/Wasm.Build.Tests.csproj
index 3941d5dfca1..7a9c16c6a34 100644
--- a/src/mono/wasm/Wasm.Build.Tests/Wasm.Build.Tests.csproj
+++ b/src/mono/wasm/Wasm.Build.Tests/Wasm.Build.Tests.csproj
@@ -77,7 +77,8 @@
<ItemGroup>
<_RuntimePackVersions Include="$(PackageVersion)" EnvVarName="RUNTIME_PACK_VER8" />
- <_RuntimePackVersions Include="$(PackageVersionNet7)" EnvVarName="RUNTIME_PACK_VER7" />
+ <_RuntimePackVersions Include="$(PackageVersionNet7)" EnvVarName="RUNTIME_PACK_VER7" Condition="'$(PackageVersionNet7)' != ''"/>
+ <_RuntimePackVersions Include="$(PackageVersion)" EnvVarName="RUNTIME_PACK_VER7" Condition="'$(PackageVersionNet7)' == ''"/>
<_RuntimePackVersions Include="$(PackageVersionNet6)" EnvVarName="RUNTIME_PACK_VER6" />
<RunScriptCommands Condition="'$(OS)' != 'Windows_NT'" Include="export %(_RuntimePackVersions.EnvVarName)=&quot;%(_RuntimePackVersions.Identity)&quot;" />
diff --git a/src/mono/wasm/Wasm.Build.Tests/WasmTemplateTests.cs b/src/mono/wasm/Wasm.Build.Tests/WasmTemplateTests.cs
index e96ed8b797e..01ea2a6d9d1 100644
--- a/src/mono/wasm/Wasm.Build.Tests/WasmTemplateTests.cs
+++ b/src/mono/wasm/Wasm.Build.Tests/WasmTemplateTests.cs
@@ -55,8 +55,7 @@ namespace Wasm.Build.Tests
string mainJsContent = File.ReadAllText(mainJsPath);
mainJsContent = mainJsContent
- .Replace(".create()", ".withConsoleForwarding().create()")
- .Replace("[\"dotnet\", \"is\", \"great!\"]", "(await import(/* webpackIgnore: true */\"process\")).argv.slice(2)");
+ .Replace(".create()", ".withConsoleForwarding().create()");
File.WriteAllText(mainJsPath, mainJsContent);
}
@@ -185,10 +184,19 @@ namespace Wasm.Build.Tests
[InlineData("Debug", true)]
[InlineData("Release", false)]
[InlineData("Release", true)]
- public void ConsoleBuildAndRun(string config, bool relinking)
+ public void ConsoleBuildAndRunDefault(string config, bool relinking)
+ => ConsoleBuildAndRun(config, relinking, string.Empty);
+
+ [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
+ [InlineData("Debug", "-f net7.0")]
+ [InlineData("Debug", "-f net8.0")]
+ public void ConsoleBuildAndRunForSpecificTFM(string config, string extraNewArgs)
+ => ConsoleBuildAndRun(config, false, extraNewArgs);
+
+ private void ConsoleBuildAndRun(string config, bool relinking, string extraNewArgs)
{
string id = $"{config}_{Path.GetRandomFileName()}";
- string projectFile = CreateWasmTemplateProject(id, "wasmconsole");
+ string projectFile = CreateWasmTemplateProject(id, "wasmconsole", extraNewArgs);
string projectName = Path.GetFileNameWithoutExtension(projectFile);
UpdateProgramCS();
@@ -214,9 +222,24 @@ namespace Wasm.Build.Tests
(int exitCode, string output) = RunProcess(s_buildEnv.DotNet, _testOutput, args: $"run --no-build -c {config} x y z", workingDir: _projectDir);
Assert.Equal(42, exitCode);
- Assert.Contains("args[0] = x", output);
- Assert.Contains("args[1] = y", output);
- Assert.Contains("args[2] = z", output);
+
+ try
+ {
+ Assert.Contains("args[0] = x", output);
+ Assert.Contains("args[1] = y", output);
+ Assert.Contains("args[2] = z", output);
+ }
+ catch
+ {
+ if (!extraNewArgs.Contains("-f net7.0"))
+ throw;
+
+ // Workaround for https://github.com/dotnet/runtime/issues/76429
+ // till a 7.0 sdk with the fix becomes available
+ Assert.Contains("args[0] = dotnet", output);
+ Assert.Contains("args[1] = is", output);
+ Assert.Contains("args[2] = great!", output);
+ }
}
public static TheoryData<bool, bool, string> TestDataForAppBundleDir()
@@ -234,9 +257,9 @@ namespace Wasm.Build.Tests
//data.Add(runOutsideProjectDirectory, forConsole, string.Empty);
data.Add(runOutsideProjectDirectory, forConsole,
- $"<OutputPath>{Path.Combine(Path.GetTempPath(), Path.GetRandomFileName())}</OutputPath>");
+ $"<OutputPath>{Path.Combine(BuildEnvironment.TmpPath, Path.GetRandomFileName())}</OutputPath>");
data.Add(runOutsideProjectDirectory, forConsole,
- $"<WasmAppDir>{Path.Combine(Path.GetTempPath(), Path.GetRandomFileName())}</WasmAppDir>");
+ $"<WasmAppDir>{Path.Combine(BuildEnvironment.TmpPath, Path.GetRandomFileName())}</WasmAppDir>");
}
return data;
@@ -259,13 +282,13 @@ namespace Wasm.Build.Tests
if (!string.IsNullOrEmpty(extraProperties))
AddItemsPropertiesToProject(projectFile, extraProperties: extraProperties);
- string workingDir = runOutsideProjectDirectory ? Path.GetTempPath() : _projectDir!;
+ string workingDir = runOutsideProjectDirectory ? BuildEnvironment.TmpPath : _projectDir!;
{
using var runCommand = new RunCommand(s_buildEnv, _testOutput)
.WithWorkingDirectory(workingDir);
- await using var runner = new BrowserRunner();
+ await using var runner = new BrowserRunner(_testOutput);
var page = await runner.RunAsync(runCommand, $"run -c {config} --project {projectFile} --forward-console");
await runner.WaitForExitMessageAsync(TimeSpan.FromMinutes(2));
Assert.Contains("Hello, Browser!", string.Join(Environment.NewLine, runner.OutputLines));
@@ -275,7 +298,7 @@ namespace Wasm.Build.Tests
using var runCommand = new RunCommand(s_buildEnv, _testOutput)
.WithWorkingDirectory(workingDir);
- await using var runner = new BrowserRunner();
+ await using var runner = new BrowserRunner(_testOutput);
var page = await runner.RunAsync(runCommand, $"run -c {config} --no-build --project {projectFile} --forward-console");
await runner.WaitForExitMessageAsync(TimeSpan.FromMinutes(2));
Assert.Contains("Hello, Browser!", string.Join(Environment.NewLine, runner.OutputLines));
@@ -293,7 +316,7 @@ namespace Wasm.Build.Tests
if (!string.IsNullOrEmpty(extraProperties))
AddItemsPropertiesToProject(projectFile, extraProperties: extraProperties);
- string workingDir = runOutsideProjectDirectory ? Path.GetTempPath() : _projectDir!;
+ string workingDir = runOutsideProjectDirectory ? BuildEnvironment.TmpPath : _projectDir!;
{
string runArgs = $"run -c {config} --project {projectFile}";
@@ -420,7 +443,7 @@ namespace Wasm.Build.Tests
using var runCommand = new RunCommand(s_buildEnv, _testOutput)
.WithWorkingDirectory(_projectDir!);
- await using var runner = new BrowserRunner();
+ await using var runner = new BrowserRunner(_testOutput);
var page = await runner.RunAsync(runCommand, $"run -c {config} --no-build");
await page.Locator("text=Counter").ClickAsync();
@@ -432,12 +455,15 @@ namespace Wasm.Build.Tests
Assert.Equal("Current count: 1", txt);
}
- [ConditionalFact(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
- public async Task BrowserTest()
+ [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
+ [InlineData("")]
+ [InlineData("-f net7.0")]
+ [InlineData("-f net8.0")]
+ public async Task BrowserBuildAndRun(string extraNewArgs)
{
string config = "Debug";
string id = $"browser_{config}_{Path.GetRandomFileName()}";
- CreateWasmTemplateProject(id, "wasmbrowser");
+ CreateWasmTemplateProject(id, "wasmbrowser", extraNewArgs);
UpdateBrowserMainJs(DefaultTargetFramework);
@@ -449,7 +475,7 @@ namespace Wasm.Build.Tests
using var runCommand = new RunCommand(s_buildEnv, _testOutput)
.WithWorkingDirectory(_projectDir!);
- await using var runner = new BrowserRunner();
+ await using var runner = new BrowserRunner(_testOutput);
var page = await runner.RunAsync(runCommand, $"run -c {config} --no-build -r browser-wasm --forward-console");
await runner.WaitForExitMessageAsync(TimeSpan.FromMinutes(2));
Assert.Contains("Hello, Browser!", string.Join(Environment.NewLine, runner.OutputLines));
diff --git a/src/mono/wasm/build/WasmApp.targets b/src/mono/wasm/build/WasmApp.targets
index 811e858ca83..fbd16363dd9 100644
--- a/src/mono/wasm/build/WasmApp.targets
+++ b/src/mono/wasm/build/WasmApp.targets
@@ -115,6 +115,11 @@
<WasmDebugLevel Condition="('$(WasmDebugLevel)' == '' or '$(WasmDebugLevel)' == '0') and ('$(DebuggerSupport)' == 'true' or '$(Configuration)' == 'Debug')">-1</WasmDebugLevel>
</PropertyGroup>
+ <ItemGroup>
+ <!-- Allow running/debugging from VS -->
+ <ProjectCapability Include="DotNetCoreWeb"/>
+ </ItemGroup>
+
<PropertyGroup Label="Identify app bundle directory to run from">
<!-- Allow running from custom WasmAppDir -->
<_AppBundleDirForRunCommand Condition="'$(WasmAppDir)' != ''">$(WasmAppDir)</_AppBundleDirForRunCommand>
diff --git a/src/mono/wasm/debugger/BrowserDebugHost/DebugProxyHost.cs b/src/mono/wasm/debugger/BrowserDebugHost/DebugProxyHost.cs
index be3095eeffc..b97ffe7ef0f 100644
--- a/src/mono/wasm/debugger/BrowserDebugHost/DebugProxyHost.cs
+++ b/src/mono/wasm/debugger/BrowserDebugHost/DebugProxyHost.cs
@@ -1,7 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.ExceptionServices;
diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MemberObjectsExplorer.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MemberObjectsExplorer.cs
index 24f4d8522cb..0a854864306 100644
--- a/src/mono/wasm/debugger/BrowserDebugProxy/MemberObjectsExplorer.cs
+++ b/src/mono/wasm/debugger/BrowserDebugProxy/MemberObjectsExplorer.cs
@@ -64,13 +64,8 @@ namespace BrowserDebugProxy
typePropertiesBrowsableInfo.TryGetValue(field.Name, out state);
}
fieldValue["__state"] = state?.ToString();
-
- fieldValue["__section"] = field.Attributes switch
- {
- FieldAttributes.Private => "private",
- FieldAttributes.Public => "result",
- _ => "internal"
- };
+ fieldValue["__section"] = field.Attributes.HasFlag(FieldAttributes.Private)
+ ? "private" : "result";
if (field.IsBackingField)
{
@@ -238,6 +233,9 @@ namespace BrowserDebugProxy
JObject fieldValue = await ReadFieldValue(sdbHelper, retDebuggerCmdReader, field, id.Value, typeInfo, valtype, isOwn, parentTypeId, getCommandOptions, token);
numFieldsRead++;
+ if (typeInfo.Info.IsNonUserCode && getCommandOptions.HasFlag(GetObjectCommandOptions.JustMyCode) && field.Attributes.HasFlag(FieldAttributes.Private))
+ continue;
+
if (!Enum.TryParse(fieldValue["__state"].Value<string>(), out DebuggerBrowsableState fieldState)
|| fieldState == DebuggerBrowsableState.Collapsed)
{
@@ -311,7 +309,7 @@ namespace BrowserDebugProxy
int typeId,
string typeName,
ArraySegment<byte> getterParamsBuffer,
- bool isAutoExpandable,
+ GetObjectCommandOptions getCommandOptions,
DotnetObjectId objectId,
bool isValueType,
bool isOwn,
@@ -347,6 +345,10 @@ namespace BrowserDebugProxy
MethodAttributes getterAttrs = getterInfo.Info.Attributes;
MethodAttributes getterMemberAccessAttrs = getterAttrs & MethodAttributes.MemberAccessMask;
MethodAttributes vtableLayout = getterAttrs & MethodAttributes.VtableLayoutMask;
+
+ if (typeInfo.Info.IsNonUserCode && getCommandOptions.HasFlag(GetObjectCommandOptions.JustMyCode) && getterMemberAccessAttrs == MethodAttributes.Private)
+ continue;
+
bool isNewSlot = (vtableLayout & MethodAttributes.NewSlot) == MethodAttributes.NewSlot;
typePropertiesBrowsableInfo.TryGetValue(propName, out DebuggerBrowsableState? state);
@@ -425,8 +427,7 @@ namespace BrowserDebugProxy
backingField["__section"] = getterMemberAccessAttrs switch
{
MethodAttributes.Private => "private",
- MethodAttributes.Public => "result",
- _ => "internal"
+ _ => "result"
};
backingField["__state"] = state?.ToString();
@@ -454,7 +455,7 @@ namespace BrowserDebugProxy
{
string returnTypeName = await sdbHelper.GetReturnType(getMethodId, token);
JObject propRet = null;
- if (isAutoExpandable || (state is DebuggerBrowsableState.RootHidden && IsACollectionType(returnTypeName)))
+ if (getCommandOptions.HasFlag(GetObjectCommandOptions.AutoExpandable) || getCommandOptions.HasFlag(GetObjectCommandOptions.ForDebuggerProxyAttribute) || (state is DebuggerBrowsableState.RootHidden && IsACollectionType(returnTypeName)))
{
try
{
@@ -474,8 +475,7 @@ namespace BrowserDebugProxy
propRet["__section"] = getterAttrs switch
{
MethodAttributes.Private => "private",
- MethodAttributes.Public => "result",
- _ => "internal"
+ _ => "result"
};
propRet["__state"] = state?.ToString();
if (parentTypeId != -1)
@@ -568,10 +568,6 @@ namespace BrowserDebugProxy
for (int i = 0; i < typeIdsCnt; i++)
{
int typeId = typeIdsIncludingParents[i];
- var typeInfo = await sdbHelper.GetTypeInfo(typeId, token);
-
- if (typeInfo.Info.IsNonUserCode && getCommandType.HasFlag(GetObjectCommandOptions.JustMyCode))
- continue;
int parentTypeId = i + 1 < typeIdsCnt ? typeIdsIncludingParents[i + 1] : -1;
string typeName = await sdbHelper.GetTypeName(typeId, token);
@@ -604,7 +600,7 @@ namespace BrowserDebugProxy
typeId,
typeName,
getPropertiesParamBuffer,
- getCommandType.HasFlag(GetObjectCommandOptions.ForDebuggerProxyAttribute),
+ getCommandType,
id,
isValueType: false,
isOwn,
@@ -656,25 +652,21 @@ namespace BrowserDebugProxy
internal sealed class GetMembersResult
{
- // public:
+ // public / protected / internal:
public JArray Result { get; set; }
// private:
public JArray PrivateMembers { get; set; }
- // protected / internal:
- public JArray OtherMembers { get; set; }
public JObject JObject => JObject.FromObject(new
{
result = Result,
- privateProperties = PrivateMembers,
- internalProperties = OtherMembers
+ privateProperties = PrivateMembers
});
public GetMembersResult()
{
Result = new JArray();
PrivateMembers = new JArray();
- OtherMembers = new JArray();
}
public GetMembersResult(JArray value, bool sortByAccessLevel)
@@ -682,7 +674,6 @@ namespace BrowserDebugProxy
var t = FromValues(value, sortByAccessLevel);
Result = t.Result;
PrivateMembers = t.PrivateMembers;
- OtherMembers = t.OtherMembers;
}
public static GetMembersResult FromValues(IEnumerable<JToken> values, bool splitMembersByAccessLevel = false) =>
@@ -717,9 +708,6 @@ namespace BrowserDebugProxy
case "private":
PrivateMembers.Add(member);
return;
- case "internal":
- OtherMembers.Add(member);
- return;
default:
Result.Add(member);
return;
@@ -730,7 +718,6 @@ namespace BrowserDebugProxy
{
Result = (JArray)Result.DeepClone(),
PrivateMembers = (JArray)PrivateMembers.DeepClone(),
- OtherMembers = (JArray)OtherMembers.DeepClone()
};
public IEnumerable<JToken> Where(Func<JToken, bool> predicate)
@@ -749,26 +736,17 @@ namespace BrowserDebugProxy
yield return item;
}
}
- foreach (var item in OtherMembers)
- {
- if (predicate(item))
- {
- yield return item;
- }
- }
}
internal JToken FirstOrDefault(Func<JToken, bool> p)
=> Result.FirstOrDefault(p)
- ?? PrivateMembers.FirstOrDefault(p)
- ?? OtherMembers.FirstOrDefault(p);
+ ?? PrivateMembers.FirstOrDefault(p);
internal JArray Flatten()
{
var result = new JArray();
result.AddRange(Result);
result.AddRange(PrivateMembers);
- result.AddRange(OtherMembers);
return result;
}
public override string ToString() => $"{JObject}\n";
diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs
index cbe0692125b..902d339da85 100644
--- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs
+++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs
@@ -60,7 +60,8 @@ namespace Microsoft.WebAssembly.Diagnostics
ForDebuggerProxyAttribute = 8,
ForDebuggerDisplayAttribute = 16,
WithProperties = 32,
- JustMyCode = 64
+ JustMyCode = 64,
+ AutoExpandable = 128
}
internal enum CommandSet {
@@ -1608,6 +1609,14 @@ namespace Microsoft.WebAssembly.Diagnostics
{
dispAttrStr = dispAttrStr.Replace(",nq", "");
}
+ if (dispAttrStr.Contains(", raw"))
+ {
+ dispAttrStr = dispAttrStr.Replace(", raw", "");
+ }
+ if (dispAttrStr.Contains(",raw"))
+ {
+ dispAttrStr = dispAttrStr.Replace(",raw", "");
+ }
expr = "$\"" + dispAttrStr + "\"";
JObject retValue = await resolver.Resolve(expr, token);
retValue ??= await ExpressionEvaluator.CompileAndRunTheExpression(expr, resolver, logger, token);
diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/ValueTypeClass.cs b/src/mono/wasm/debugger/BrowserDebugProxy/ValueTypeClass.cs
index ebbcb6c5d3d..865785bf51e 100644
--- a/src/mono/wasm/debugger/BrowserDebugProxy/ValueTypeClass.cs
+++ b/src/mono/wasm/debugger/BrowserDebugProxy/ValueTypeClass.cs
@@ -98,9 +98,7 @@ namespace BrowserDebugProxy
if (isStatic)
fieldValue["name"] = field.Name;
FieldAttributes attr = field.Attributes & FieldAttributes.FieldAccessMask;
- fieldValue["__section"] = attr == FieldAttributes.Public
- ? "public" :
- attr == FieldAttributes.Private ? "private" : "internal";
+ fieldValue["__section"] = attr == FieldAttributes.Private ? "private" : "result";
if (field.IsBackingField)
{
@@ -218,7 +216,6 @@ namespace BrowserDebugProxy
result = _combinedResult.Clone();
RemovePropertiesFrom(result.Result);
RemovePropertiesFrom(result.PrivateMembers);
- RemovePropertiesFrom(result.OtherMembers);
}
// 4 - fields + properties
@@ -290,7 +287,7 @@ namespace BrowserDebugProxy
typeId,
className,
Buffer,
- autoExpand,
+ autoExpand ? GetObjectCommandOptions.AutoExpandable : GetObjectCommandOptions.None,
Id,
isValueType: true,
isOwn: i == 0,
diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs
index 555c0cf744d..b533d39910a 100644
--- a/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs
+++ b/src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs
@@ -988,7 +988,7 @@ namespace DebuggerTests
return locals;
}
- internal async Task<(JToken, JToken, JToken)> GetPropertiesSortedByProtectionLevels(string id, JToken fn_args = null, bool? own_properties = null, bool? accessors_only = null, bool expect_ok = true)
+ internal async Task<(JToken, JToken)> GetPropertiesSortedByProtectionLevels(string id, JToken fn_args = null, bool? own_properties = null, bool? accessors_only = null, bool expect_ok = true)
{
if (UseCallFunctionOnBeforeGetProperties && !id.StartsWith("dotnet:scope:"))
{
@@ -1004,7 +1004,7 @@ namespace DebuggerTests
var result = await cli.SendCommand("Runtime.callFunctionOn", cfo_args, token);
AssertEqual(expect_ok, result.IsOk, $"Runtime.getProperties returned {result.IsOk} instead of {expect_ok}, for {cfo_args.ToString()}, with Result: {result}");
if (!result.IsOk)
- return (null, null, null);
+ return (null, null);
id = result.Value["result"]?["objectId"]?.Value<string>();
}
@@ -1024,10 +1024,9 @@ namespace DebuggerTests
var frame_props = await cli.SendCommand("Runtime.getProperties", get_prop_req, token);
AssertEqual(expect_ok, frame_props.IsOk, $"Runtime.getProperties returned {frame_props.IsOk} instead of {expect_ok}, for {get_prop_req}, with Result: {frame_props}");
if (!frame_props.IsOk)
- return (null, null, null);;
+ return (null, null);;
var locals = frame_props.Value["result"];
- var locals_internal = frame_props.Value["internalProperties"];
var locals_private = frame_props.Value["privateProperties"];
// FIXME: Should be done when generating the list in dotnet.es6.lib.js, but not sure yet
@@ -1044,7 +1043,7 @@ namespace DebuggerTests
}
}
- return (locals, locals_internal, locals_private);
+ return (locals, locals_private);
}
internal virtual async Task<(JToken, Result)> EvaluateOnCallFrame(string id, string expression, bool expect_ok = true)
diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/GetPropertiesTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/GetPropertiesTests.cs
index d9fc2098dc0..3bf376c8954 100644
--- a/src/mono/wasm/debugger/DebuggerTestSuite/GetPropertiesTests.cs
+++ b/src/mono/wasm/debugger/DebuggerTestSuite/GetPropertiesTests.cs
@@ -26,6 +26,7 @@ namespace DebuggerTests
var type_name = "DerivedClass2";
var all_props = new Dictionary<string, (JObject, bool)>()
{
+ // ------------------instance members--------------------------
// own:
// public:
{"BaseBase_PropertyForHidingWithField", (TNumber(210), true)},
@@ -80,6 +81,45 @@ namespace DebuggerTests
{"BaseBase_AutoPropertyForHidingWithAutoProperty (BaseBaseClass2)", (TString("BaseBase#BaseBase_AutoPropertyForHidingWithAutoProperty"), false)},
{"BaseBase_PropertyForVHO (BaseBaseClass2)", (TGetter("BaseBase_PropertyForVHO (BaseBaseClass2)", TString("BaseBase#BaseBase_PropertyForVHO")), false)},
{"BaseBase_AutoPropertyForVHO (BaseBaseClass2)", (TString("BaseBase#BaseBase_AutoPropertyForVHO"), false)},
+
+ // ------------------static members--------------------------
+ // own:
+ // public:
+ {"S_BaseBase_PropertyForHidingWithField", (TNumber(210), true)},
+
+ // protected / internal:
+ {"S_BaseBase_AutoPropertyForHidingWithProperty", (TGetter("S_BaseBase_AutoPropertyForHidingWithProperty", TString("Derived#BaseBase_AutoPropertyForHidingWithProperty")), true)},
+
+ // private:
+ {"S_BaseBase_FieldForHidingWithAutoProperty", (TString(null), true)},
+
+ // inherited from Base:
+ // public:
+ {"S_BaseBase_AutoPropertyForHidingWithField", (TNumber(115), false)},
+ {"S_BaseBase_PropertyForHidingWithProperty", (TGetter("S_BaseBase_PropertyForHidingWithProperty", TString("Base#BaseBase_PropertyForHidingWithProperty")), false)},
+ {"S_BaseBase_FieldForHidingWithAutoProperty (BaseClass2)", (TString(null), false)},
+
+ // protected / internal:
+ {"S_BaseBase_PropertyForHidingWithField (BaseClass2)", (TNumber(110), false)},
+ {"S_BaseBase_FieldForHidingWithProperty", (TGetter("S_BaseBase_FieldForHidingWithProperty", TString("Base#BaseBase_FieldForHidingWithProperty")), false)},
+ {"S_BaseBase_AutoPropertyForHidingWithAutoProperty", (TString(null), false)},
+
+ // private:
+ {"S_BaseBase_FieldForHidingWithField", (TNumber(105), false)},
+ {"S_BaseBase_AutoPropertyForHidingWithProperty (BaseClass2)", (TGetter("S_BaseBase_AutoPropertyForHidingWithProperty (BaseClass2)", TString("Base#BaseBase_AutoPropertyForHidingWithProperty")), false)},
+ {"S_BaseBase_PropertyForHidingWithAutoProperty", (TString(null), false)},
+
+ // inherited from BaseBase:
+ // public:
+ {"S_BaseBase_FieldForHidingWithField (BaseBaseClass2)", (TNumber(5), false)},
+ {"S_BaseBase_PropertyForHidingWithField (BaseBaseClass2)", (TGetter("S_BaseBase_PropertyForHidingWithField (BaseBaseClass2)", TNumber(10)), false)},
+ {"S_BaseBase_AutoPropertyForHidingWithField (BaseBaseClass2)", (TNumber(0), false)},
+ {"S_BaseBase_FieldForHidingWithProperty (BaseBaseClass2)", (TString("BaseBase#BaseBase_FieldForHidingWithProperty"), false)},
+ {"S_BaseBase_PropertyForHidingWithProperty (BaseBaseClass2)", (TGetter("S_BaseBase_PropertyForHidingWithProperty (BaseBaseClass2)", TString("BaseBase#BaseBase_PropertyForHidingWithProperty")), false)},
+ {"S_BaseBase_AutoPropertyForHidingWithProperty (BaseBaseClass2)", (TString(null), false)},
+ {"S_BaseBase_FieldForHidingWithAutoProperty (BaseBaseClass2)", (TString("BaseBase#BaseBase_FieldForHidingWithAutoProperty"), false)},
+ {"S_BaseBase_PropertyForHidingWithAutoProperty (BaseBaseClass2)", (TGetter("S_BaseBase_PropertyForHidingWithAutoProperty (BaseBaseClass2)", TString("BaseBase#BaseBase_PropertyForHidingWithAutoProperty")), false)},
+ {"S_BaseBase_AutoPropertyForHidingWithAutoProperty (BaseBaseClass2)", (TString(null), false)},
};
// default, all properties
@@ -109,6 +149,7 @@ namespace DebuggerTests
"BaseBase_PropertyForVHO",
"BaseBase_PropertyForVOH",
// "BaseBase_PropertyForVOO", // FixMe: Issue #69788
+ "S_BaseBase_AutoPropertyForHidingWithProperty",
"BaseBase_PropertyForHidingWithProperty",
"FirstName",
@@ -116,12 +157,18 @@ namespace DebuggerTests
"BaseBase_FieldForHidingWithProperty",
"BaseBase_AutoPropertyForHidingWithProperty (BaseClass2)",
"BaseBase_PropertyForVOH (BaseClass2)",
+ "S_BaseBase_PropertyForHidingWithProperty",
+ "S_BaseBase_FieldForHidingWithProperty",
+ "S_BaseBase_AutoPropertyForHidingWithProperty (BaseClass2)",
"BaseBase_PropertyForHidingWithField (BaseBaseClass2)",
"BaseBase_PropertyForHidingWithProperty (BaseBaseClass2)",
"BaseBase_PropertyForHidingWithAutoProperty (BaseBaseClass2)",
"Base_VirtualPropertyNotOverriddenOrHidden",
- "BaseBase_PropertyForVHO (BaseBaseClass2)"
+ "BaseBase_PropertyForVHO (BaseBaseClass2)",
+ "S_BaseBase_PropertyForHidingWithField (BaseBaseClass2)",
+ "S_BaseBase_PropertyForHidingWithProperty (BaseBaseClass2)",
+ "S_BaseBase_PropertyForHidingWithAutoProperty (BaseBaseClass2)"
};
var only_own_accessors = new[]
@@ -132,6 +179,7 @@ namespace DebuggerTests
"BaseBase_PropertyForVHO",
"BaseBase_PropertyForVOH",
// "BaseBase_PropertyForVOO", // FixMe: Issue #69788
+ "S_BaseBase_AutoPropertyForHidingWithProperty",
};
// all own, only accessors
@@ -449,12 +497,13 @@ namespace DebuggerTests
throw new XunitException($"missing or unexpected members found");
}
- public static TheoryData<Dictionary<string, JObject>, Dictionary<string, JObject>, Dictionary<string, JObject>, string> GetDataForProtectionLevels()
+ public static TheoryData<Dictionary<string, JObject>, Dictionary<string, JObject>, string> GetDataForProtectionLevels()
{
- var data = new TheoryData<Dictionary<string, JObject>, Dictionary<string, JObject>, Dictionary<string, JObject>, string>();
+ var data = new TheoryData<Dictionary<string, JObject>, Dictionary<string, JObject>, string>();
var public_props = new Dictionary<string, JObject>()
{
+ // --------- public ------------:
// own:
{"BaseBase_PropertyForHidingWithField", TNumber(210)},
{"Base_PropertyForOverridingWithProperty", TGetter("Base_PropertyForOverridingWithProperty", TDateTime(new DateTime(2020, 7, 6, 5, 4, 3)))},
@@ -464,6 +513,7 @@ namespace DebuggerTests
{"BaseBase_AutoPropertyForVHO", TString("Derived#BaseBase_AutoPropertyForVHO")},
{"BaseBase_AutoPropertyForVOH", TString("Derived#BaseBase_AutoPropertyForVOH")},
// {"BaseBase_AutoPropertyForVOO", TString("Derived#BaseBase_AutoPropertyForVOO")}, //FixMe: Issue #69788
+ {"S_BaseBase_PropertyForHidingWithField", TNumber(210)},
// inherited from Base:
{"BaseBase_AutoPropertyForHidingWithField", TNumber(115)},
@@ -474,6 +524,9 @@ namespace DebuggerTests
{"Base_VirtualPropertyNotOverriddenOrHidden", TGetter("Base_VirtualPropertyNotOverriddenOrHidden", TDateTime(new DateTime(2124, 5, 7, 1, 9, 2)))},
{"BaseBase_PropertyForVOH (BaseClass2)", TGetter("BaseBase_PropertyForVOH (BaseClass2)", TString("Base#BaseBase_PropertyForVOH"))},
{"BaseBase_AutoPropertyForVOH (BaseClass2)", TString("Base#BaseBase_AutoPropertyForVOH")},
+ {"S_BaseBase_AutoPropertyForHidingWithField", TNumber(115)},
+ {"S_BaseBase_PropertyForHidingWithProperty", TGetter("S_BaseBase_PropertyForHidingWithProperty", TString("Base#BaseBase_PropertyForHidingWithProperty"))},
+ {"S_BaseBase_FieldForHidingWithAutoProperty (BaseClass2)", TString(null)},
// inherited from BaseBase:
{"BaseBase_FieldForHidingWithField (BaseBaseClass2)", TNumber(5)},
@@ -487,43 +540,57 @@ namespace DebuggerTests
{"BaseBase_AutoPropertyForHidingWithAutoProperty (BaseBaseClass2)", TString("BaseBase#BaseBase_AutoPropertyForHidingWithAutoProperty")},
{"BaseBase_PropertyForVHO (BaseBaseClass2)", TGetter("BaseBase_PropertyForVHO (BaseBaseClass2)", TString("BaseBase#BaseBase_PropertyForVHO"))},
{"BaseBase_AutoPropertyForVHO (BaseBaseClass2)", TString("BaseBase#BaseBase_AutoPropertyForVHO")},
- };
-
- var internal_protected_props = new Dictionary<string, JObject>(){
-
+ {"S_BaseBase_FieldForHidingWithField (BaseBaseClass2)", TNumber(5)},
+ {"S_BaseBase_PropertyForHidingWithField (BaseBaseClass2)", TGetter("S_BaseBase_PropertyForHidingWithField (BaseBaseClass2)", TNumber(10))},
+ {"S_BaseBase_AutoPropertyForHidingWithField (BaseBaseClass2)", TNumber(0)},
+ {"S_BaseBase_FieldForHidingWithProperty (BaseBaseClass2)", TString("BaseBase#BaseBase_FieldForHidingWithProperty")},
+ {"S_BaseBase_PropertyForHidingWithProperty (BaseBaseClass2)", TGetter("S_BaseBase_PropertyForHidingWithProperty (BaseBaseClass2)", TString("BaseBase#BaseBase_PropertyForHidingWithProperty"))},
+ {"S_BaseBase_AutoPropertyForHidingWithProperty (BaseBaseClass2)", TString(null)},
+ {"S_BaseBase_FieldForHidingWithAutoProperty (BaseBaseClass2)", TString("BaseBase#BaseBase_FieldForHidingWithAutoProperty")},
+ {"S_BaseBase_PropertyForHidingWithAutoProperty (BaseBaseClass2)", TGetter("S_BaseBase_PropertyForHidingWithAutoProperty (BaseBaseClass2)", TString("BaseBase#BaseBase_PropertyForHidingWithAutoProperty"))},
+ {"S_BaseBase_AutoPropertyForHidingWithAutoProperty (BaseBaseClass2)",TString(null)},
+
+ // ---- internal / protected ----:
// own:
{"BaseBase_AutoPropertyForHidingWithProperty", TGetter("BaseBase_AutoPropertyForHidingWithProperty", TString("Derived#BaseBase_AutoPropertyForHidingWithProperty"))},
{"Base_PropertyForOverridingWithAutoProperty", TDateTime(new DateTime(2022, 7, 6, 5, 4, 3))},
{"Base_AutoPropertyForOverridingWithAutoProperty", TDateTime(new DateTime(2023, 7, 6, 5, 4, 3))},
{"Base_AutoPropertyForOverridingWithProperty", TGetter("Base_AutoPropertyForOverridingWithProperty", TDateTime(new DateTime(2021, 7, 6, 5, 4, 3)))},
+ {"S_BaseBase_AutoPropertyForHidingWithProperty", TGetter("S_BaseBase_AutoPropertyForHidingWithProperty", TString("Derived#BaseBase_AutoPropertyForHidingWithProperty"))},
// inherited from Base:
{"BaseBase_PropertyForHidingWithField (BaseClass2)", TNumber(110)},
{"BaseBase_FieldForHidingWithProperty", TGetter("BaseBase_FieldForHidingWithProperty", TString("Base#BaseBase_FieldForHidingWithProperty"))},
- {"BaseBase_AutoPropertyForHidingWithAutoProperty", TString("Base#BaseBase_AutoPropertyForHidingWithAutoProperty")}
+ {"BaseBase_AutoPropertyForHidingWithAutoProperty", TString("Base#BaseBase_AutoPropertyForHidingWithAutoProperty")},
+ {"S_BaseBase_PropertyForHidingWithField (BaseClass2)", TNumber(110)},
+ {"S_BaseBase_FieldForHidingWithProperty", TGetter("S_BaseBase_FieldForHidingWithProperty", TString("Base#BaseBase_FieldForHidingWithProperty"))},
+ {"S_BaseBase_AutoPropertyForHidingWithAutoProperty", TString(null)},
};
var private_props = new Dictionary<string, JObject>(){
// own
- {"BaseBase_FieldForHidingWithAutoProperty", TString("Derived#BaseBase_FieldForHidingWithAutoProperty")},
+ {"BaseBase_FieldForHidingWithAutoProperty", TString("Derived#BaseBase_FieldForHidingWithAutoProperty")},
+ {"S_BaseBase_FieldForHidingWithAutoProperty", TString(null)},
// from Base:
- {"BaseBase_FieldForHidingWithField", TNumber(105)},
- {"BaseBase_AutoPropertyForHidingWithProperty (BaseClass2)", TGetter("BaseBase_AutoPropertyForHidingWithProperty (BaseClass2)", TString("Base#BaseBase_AutoPropertyForHidingWithProperty"))},
- {"BaseBase_PropertyForHidingWithAutoProperty", TString("Base#BaseBase_PropertyForHidingWithAutoProperty")},
+ {"BaseBase_FieldForHidingWithField", TNumber(105)},
+ {"BaseBase_AutoPropertyForHidingWithProperty (BaseClass2)", TGetter("BaseBase_AutoPropertyForHidingWithProperty (BaseClass2)", TString("Base#BaseBase_AutoPropertyForHidingWithProperty"))},
+ {"BaseBase_PropertyForHidingWithAutoProperty", TString("Base#BaseBase_PropertyForHidingWithAutoProperty")},
+ {"S_BaseBase_FieldForHidingWithField", TNumber(105)},
+ {"S_BaseBase_AutoPropertyForHidingWithProperty (BaseClass2)",TGetter("S_BaseBase_AutoPropertyForHidingWithProperty (BaseClass2)", TString("Base#BaseBase_AutoPropertyForHidingWithProperty"))},
+ {"S_BaseBase_PropertyForHidingWithAutoProperty", TString(null)},
};
- data.Add(public_props, internal_protected_props, private_props, "DerivedClass2");
+ data.Add(public_props, private_props, "DerivedClass2");
// structure CloneableStruct:
public_props = new Dictionary<string, JObject>()
{
// own
+ // public
{"a", TNumber(4)},
{"DateTime", TGetter("DateTime")},
{"AutoStringProperty", TString("CloneableStruct#AutoStringProperty")},
{"FirstName", TGetter("FirstName")},
- {"LastName", TGetter("LastName")}
- };
- internal_protected_props = new Dictionary<string, JObject>()
- {
+ {"LastName", TGetter("LastName")},
+
// internal
{"b", TBool(true)}
};
@@ -533,14 +600,14 @@ namespace DebuggerTests
{"_dateTime", TDateTime(new DateTime(2020, 7, 6, 5, 4, 3 + 3))},
{"_DTProp", TGetter("_DTProp")}
};
- data.Add(public_props, internal_protected_props, private_props, "CloneableStruct");
+ data.Add(public_props, private_props, "CloneableStruct");
return data;
}
[ConditionalTheory(nameof(RunningOnChrome))]
[MemberData(nameof(GetDataForProtectionLevels))]
public async Task PropertiesSortedByProtectionLevel(
- Dictionary<string, JObject> expectedPublic, Dictionary<string, JObject> expectedProtInter, Dictionary<string, JObject> expectedPriv, string entryMethod) =>
+ Dictionary<string, JObject> expectedPublicInternalAndProtected, Dictionary<string, JObject> expectedPriv, string entryMethod) =>
await CheckInspectLocalsAtBreakpointSite(
$"DebuggerTests.GetPropertiesTests.{entryMethod}", "InstanceMethod", 1, $"DebuggerTests.GetPropertiesTests.{entryMethod}.InstanceMethod",
$"window.setTimeout(function() {{ invoke_static_method ('[debugger-test] DebuggerTests.GetPropertiesTests.{entryMethod}:run'); }})",
@@ -548,14 +615,12 @@ namespace DebuggerTests
{
var id = pause_location["callFrames"][0]["callFrameId"].Value<string>();
var (obj, _) = await EvaluateOnCallFrame(id, "this");
- var (pub, internalAndProtected, priv) = await GetPropertiesSortedByProtectionLevels(obj["objectId"]?.Value<string>());
+ var (pubInternalAndProtected, priv) = await GetPropertiesSortedByProtectionLevels(obj["objectId"]?.Value<string>());
- AssertHasOnlyExpectedProperties(expectedPublic.Keys.ToArray(), pub.Values<JObject>());
- AssertHasOnlyExpectedProperties(expectedProtInter.Keys.ToArray(), internalAndProtected.Values<JObject>());
+ AssertHasOnlyExpectedProperties(expectedPublicInternalAndProtected.Keys.ToArray(), pubInternalAndProtected.Values<JObject>());
AssertHasOnlyExpectedProperties(expectedPriv.Keys.ToArray(), priv.Values<JObject>());
- await CheckProps(pub, expectedPublic, "public");
- await CheckProps(internalAndProtected, expectedProtInter, "internalAndProtected");
+ await CheckProps(pubInternalAndProtected, expectedPublicInternalAndProtected, "result");
await CheckProps(priv, expectedPriv, "private");
});
}
diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs
index 5e1cfda9a92..d0f8be8e660 100644
--- a/src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs
+++ b/src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs
@@ -1067,7 +1067,11 @@ namespace DebuggerTests
{
myField = TNumber(0),
myField2 = TNumber(0),
- }, "this_props", num_fields: 2);
+ propB = TGetter("propB"),
+ propC = TGetter("propC"),
+ e = TNumber(50),
+ f = TNumber(60),
+ }, "this_props", num_fields: 6);
}
else
{
@@ -1100,5 +1104,30 @@ namespace DebuggerTests
bp.Value["locations"][0]["columnNumber"].Value<int>(),
$"DebuggerTests.CheckChineseCharacterInPath.Evaluate");
}
+
+ [Fact]
+ public async Task InspectReadOnlySpan()
+ {
+ var expression = $"{{ invoke_static_method('[debugger-test] ReadOnlySpanTest:Run'); }}";
+
+ await EvaluateAndCheck(
+ "window.setTimeout(function() {" + expression + "; }, 1);",
+ "dotnet://debugger-test.dll/debugger-test.cs", 1371, 8,
+ "ReadOnlySpanTest.CheckArguments",
+ wait_for_event_fn: async (pause_location) =>
+ {
+ var id = pause_location["callFrames"][0]["callFrameId"].Value<string>();
+ await EvaluateOnCallFrameAndCheck(id,
+ ("parameters.ToString()", TString("System.ReadOnlySpan<Object>[1]"))
+ );
+ }
+ );
+ await StepAndCheck(StepKind.Resume, "dotnet://debugger-test.dll/debugger-test.cs", 1363, 8, "ReadOnlySpanTest.Run",
+ locals_fn: async (locals) =>
+ {
+ await CheckValueType(locals, "var1", "System.ReadOnlySpan<object>", description: "System.ReadOnlySpan<Object>[0]");
+ }
+ );
+ }
}
}
diff --git a/src/mono/wasm/debugger/tests/debugger-test-with-non-user-code-class/test.cs b/src/mono/wasm/debugger/tests/debugger-test-with-non-user-code-class/test.cs
index af11a6329d0..1626d47d5d4 100644
--- a/src/mono/wasm/debugger/tests/debugger-test-with-non-user-code-class/test.cs
+++ b/src/mono/wasm/debugger/tests/debugger-test-with-non-user-code-class/test.cs
@@ -16,11 +16,11 @@ namespace DebuggerTests
private int d;
public int e;
protected int f;
- public int G
+ private int G
{
get {return f + 1;}
}
- public int H => f;
+ private int H => f;
public ClassNonUserCodeToInheritThatInheritsFromNormalClass()
{
diff --git a/src/mono/wasm/debugger/tests/debugger-test-without-debug-symbols-to-load/test.cs b/src/mono/wasm/debugger/tests/debugger-test-without-debug-symbols-to-load/test.cs
index 5dcdc29f93f..4899b8869ef 100644
--- a/src/mono/wasm/debugger/tests/debugger-test-without-debug-symbols-to-load/test.cs
+++ b/src/mono/wasm/debugger/tests/debugger-test-without-debug-symbols-to-load/test.cs
@@ -10,11 +10,11 @@ namespace DebuggerTests
private int d;
public int e;
protected int f;
- public int G
+ private int G
{
get {return f + 1;}
}
- public int H => f;
+ private int H => f;
public ClassWithoutDebugSymbolsToInherit()
{
diff --git a/src/mono/wasm/debugger/tests/debugger-test/debugger-get-properties-test.cs b/src/mono/wasm/debugger/tests/debugger-test/debugger-get-properties-test.cs
index e7359ee8c33..dc02bbdfb79 100644
--- a/src/mono/wasm/debugger/tests/debugger-test/debugger-get-properties-test.cs
+++ b/src/mono/wasm/debugger/tests/debugger-test/debugger-get-properties-test.cs
@@ -140,9 +140,25 @@ namespace DebuggerTests.GetPropertiesTests
public virtual string BaseBase_AutoPropertyForVHO { get; set; }
// public virtual string BaseBase_AutoPropertyForVOO { get; set; } // FixMe: Issue #69788
+ // -----------------static members--------------
+ // for new-hidding with a field:
+ public static int S_BaseBase_FieldForHidingWithField = 5;
+ public static int S_BaseBase_PropertyForHidingWithField => 10;
+ public static int S_BaseBase_AutoPropertyForHidingWithField { get; set; }
+
+ // for new-hidding with a property:
+ public static string S_BaseBase_FieldForHidingWithProperty = "BaseBase#BaseBase_FieldForHidingWithProperty";
+ public static string S_BaseBase_PropertyForHidingWithProperty => "BaseBase#BaseBase_PropertyForHidingWithProperty";
+ public static string S_BaseBase_AutoPropertyForHidingWithProperty { get; set; }
+
+ // for new-hidding with an auto-property:
+ public static string S_BaseBase_FieldForHidingWithAutoProperty = "BaseBase#BaseBase_FieldForHidingWithAutoProperty";
+ public static string S_BaseBase_PropertyForHidingWithAutoProperty => "BaseBase#BaseBase_PropertyForHidingWithAutoProperty";
+ public static string S_BaseBase_AutoPropertyForHidingWithAutoProperty { get; set; }
+
public BaseBaseClass2()
{
- BaseBase_AutoPropertyForHidingWithField = 15;
+ BaseBase_AutoPropertyForHidingWithField = 10 + S_BaseBase_FieldForHidingWithField; // = 15; suppressing non-used variable warnings
BaseBase_AutoPropertyForHidingWithProperty = "BaseBase#BaseBase_AutoPropertyForHidingWithProperty";
BaseBase_AutoPropertyForHidingWithAutoProperty = "BaseBase#BaseBase_AutoPropertyForHidingWithAutoProperty";
@@ -192,8 +208,25 @@ namespace DebuggerTests.GetPropertiesTests
public new virtual string BaseBase_AutoPropertyForVHO { get; set; }
// public override string BaseBase_AutoPropertyForVOO { get; set; }// FixMe: Issue #69788
+ // -----------------static members--------------
+ // hiding with a field:
+ private static new int S_BaseBase_FieldForHidingWithField = 105;
+ protected static new int S_BaseBase_PropertyForHidingWithField = 110;
+ public static new int S_BaseBase_AutoPropertyForHidingWithField = 115;
+
+ // hiding with a property:
+ protected static new string S_BaseBase_FieldForHidingWithProperty => "Base#BaseBase_FieldForHidingWithProperty";
+ public static new string S_BaseBase_PropertyForHidingWithProperty => "Base#BaseBase_PropertyForHidingWithProperty";
+ private static new string S_BaseBase_AutoPropertyForHidingWithProperty => "Base#BaseBase_AutoPropertyForHidingWithProperty";
+
+ // hiding with an auto-property:
+ public static new string S_BaseBase_FieldForHidingWithAutoProperty { get; set; }
+ private static new string S_BaseBase_PropertyForHidingWithAutoProperty { get; set; }
+ protected static new string S_BaseBase_AutoPropertyForHidingWithAutoProperty { get; set; }
+
public BaseClass2()
{
+ S_BaseBase_PropertyForHidingWithField = S_BaseBase_FieldForHidingWithField + 5; // suppressing non-used variable warning
BaseBase_PropertyForHidingWithField = BaseBase_FieldForHidingWithField + 5; // suppressing non-used variable warning
BaseBase_FieldForHidingWithAutoProperty = "Base#BaseBase_FieldForHidingWithAutoProperty";
BaseBase_PropertyForHidingWithAutoProperty = "Base#BaseBase_PropertyForHidingWithAutoProperty";
@@ -230,6 +263,12 @@ namespace DebuggerTests.GetPropertiesTests
public override string BaseBase_AutoPropertyForVHO { get; set; }
// public override string BaseBase_AutoPropertyForVOO { get; set; } // FixMe: Issue #69788
+ // -----------------static members--------------
+ // hiding sample members from BaseBase:
+ public static new int S_BaseBase_PropertyForHidingWithField = 210;
+ protected static new string S_BaseBase_AutoPropertyForHidingWithProperty => "Derived#BaseBase_AutoPropertyForHidingWithProperty";
+ private static new string S_BaseBase_FieldForHidingWithAutoProperty { get; set; }
+
public DerivedClass2()
{
Base_PropertyForOverridingWithAutoProperty = new (2022, 7, 6, 5, 4, 3);
diff --git a/src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs b/src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs
index b8f52c788f0..5e30c999b33 100644
--- a/src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs
+++ b/src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs
@@ -1300,11 +1300,11 @@ public class ClassNonUserCodeToInherit
private int d;
public int e;
protected int f;
- public int G
+ private int G
{
get {return f + 1;}
}
- public int H => f;
+ private int H => f;
public ClassNonUserCodeToInherit()
{
@@ -1354,4 +1354,21 @@ public class ClassInheritsFromNonUserCodeClassThatInheritsFromNormalClass : Debu
}
public int myField;
+}
+public class ReadOnlySpanTest
+{
+ public static void Run()
+ {
+ Invoke(new string[] {"TEST"});
+ ReadOnlySpan<object> var1 = new ReadOnlySpan<object>();
+ System.Diagnostics.Debugger.Break();
+ }
+ public static void Invoke(object[] parameters)
+ {
+ CheckArguments(parameters);
+ }
+ public static void CheckArguments(ReadOnlySpan<object> parameters)
+ {
+ System.Diagnostics.Debugger.Break();
+ }
} \ No newline at end of file
diff --git a/src/mono/wasm/host/BrowserHost.cs b/src/mono/wasm/host/BrowserHost.cs
index bccca9ff4f1..a592ca7386f 100644
--- a/src/mono/wasm/host/BrowserHost.cs
+++ b/src/mono/wasm/host/BrowserHost.cs
@@ -71,9 +71,13 @@ internal sealed class BrowserHost
debugging: _args.CommonConfig.Debugging);
runArgsJson.Save(Path.Combine(_args.CommonConfig.AppPath, "runArgs.json"));
+ string[] urls = envVars.TryGetValue("ASPNETCORE_URLS", out string? aspnetUrls)
+ ? aspnetUrls.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries)
+ : new string[] { $"http://127.0.0.1:{_args.CommonConfig.HostProperties.WebServerPort}", "https://127.0.0.1:0" };
+
(ServerURLs serverURLs, IWebHost host) = await StartWebServerAsync(_args.CommonConfig.AppPath,
_args.ForwardConsoleOutput ?? false,
- _args.CommonConfig.HostProperties.WebServerPort,
+ urls,
token);
string[] fullUrls = BuildUrls(serverURLs, _args.AppArgs);
@@ -84,7 +88,7 @@ internal sealed class BrowserHost
await host.WaitForShutdownAsync(token);
}
- private async Task<(ServerURLs, IWebHost)> StartWebServerAsync(string appPath, bool forwardConsole, int port, CancellationToken token)
+ private async Task<(ServerURLs, IWebHost)> StartWebServerAsync(string appPath, bool forwardConsole, string[] urls, CancellationToken token)
{
WasmTestMessagesProcessor? logProcessor = null;
if (forwardConsole)
@@ -100,7 +104,7 @@ internal sealed class BrowserHost
ContentRootPath: Path.GetFullPath(appPath),
WebServerUseCors: true,
WebServerUseCrossOriginPolicy: true,
- Port: port
+ Urls: urls
);
(ServerURLs serverURLs, IWebHost host) = await WebServer.StartAsync(options, _logger, token);
diff --git a/src/mono/wasm/host/WebServer.cs b/src/mono/wasm/host/WebServer.cs
index 51bda607167..44d02432002 100644
--- a/src/mono/wasm/host/WebServer.cs
+++ b/src/mono/wasm/host/WebServer.cs
@@ -1,13 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
-using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@@ -20,7 +16,7 @@ public class WebServer
{
internal static async Task<(ServerURLs, IWebHost)> StartAsync(WebServerOptions options, ILogger logger, CancellationToken token)
{
- string[]? urls = new string[] { $"http://127.0.0.1:{options.Port}", "https://127.0.0.1:0" };
+ TaskCompletionSource<ServerURLs> realUrlsAvailableTcs = new();
IWebHostBuilder builder = new WebHostBuilder()
.UseKestrel()
@@ -43,9 +39,10 @@ public class WebServer
}
services.AddSingleton(logger);
services.AddSingleton(Options.Create(options));
+ services.AddSingleton(realUrlsAvailableTcs);
services.AddRouting();
})
- .UseUrls(urls);
+ .UseUrls(options.Urls);
if (options.ContentRootPath != null)
builder.UseContentRoot(options.ContentRootPath);
@@ -53,27 +50,11 @@ public class WebServer
IWebHost? host = builder.Build();
await host.StartAsync(token);
- ICollection<string>? addresses = host.ServerFeatures
- .Get<IServerAddressesFeature>()?
- .Addresses;
+ if (token.CanBeCanceled)
+ token.Register(async () => await host.StopAsync());
- string? ipAddress =
- addresses?
- .Where(a => a.StartsWith("http:", StringComparison.InvariantCultureIgnoreCase))
- .Select(a => new Uri(a))
- .Select(uri => uri.ToString())
- .FirstOrDefault();
-
- string? ipAddressSecure =
- addresses?
- .Where(a => a.StartsWith("https:", StringComparison.OrdinalIgnoreCase))
- .Select(a => new Uri(a))
- .Select(uri => uri.ToString())
- .FirstOrDefault();
-
- return ipAddress == null || ipAddressSecure == null
- ? throw new InvalidOperationException("Failed to determine web server's IP address or port")
- : (new ServerURLs(ipAddress, ipAddressSecure), host);
+ ServerURLs serverUrls = await realUrlsAvailableTcs.Task;
+ return (serverUrls, host);
}
}
diff --git a/src/mono/wasm/host/WebServerOptions.cs b/src/mono/wasm/host/WebServerOptions.cs
index bc8b5f2acee..43e05c10b7c 100644
--- a/src/mono/wasm/host/WebServerOptions.cs
+++ b/src/mono/wasm/host/WebServerOptions.cs
@@ -15,6 +15,6 @@ internal sealed record WebServerOptions
string? ContentRootPath,
bool WebServerUseCors,
bool WebServerUseCrossOriginPolicy,
- int Port,
+ string [] Urls,
string DefaultFileName = "index.html"
);
diff --git a/src/mono/wasm/host/WebServerStartup.cs b/src/mono/wasm/host/WebServerStartup.cs
index ae5e906c518..64bf9e7cccb 100644
--- a/src/mono/wasm/host/WebServerStartup.cs
+++ b/src/mono/wasm/host/WebServerStartup.cs
@@ -1,12 +1,23 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
using System.Net.WebSockets;
+using System.Runtime.InteropServices;
+using System.Threading.Tasks;
+using System.Web;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.FileProviders;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
#nullable enable
@@ -16,11 +27,40 @@ namespace Microsoft.WebAssembly.AppHost;
internal sealed class WebServerStartup
{
private readonly IWebHostEnvironment _hostingEnvironment;
-
+ private static readonly object LaunchLock = new object();
+ private static string LaunchedDebugProxyUrl = "";
+ private ILogger? _logger;
public WebServerStartup(IWebHostEnvironment hostingEnvironment) => _hostingEnvironment = hostingEnvironment;
- public void Configure(IApplicationBuilder app, IOptions<WebServerOptions> optionsContainer)
+ public static int StartDebugProxy(string devToolsHost)
+ {
+ //we need to start another process, otherwise it will be running the BrowserDebugProxy in the same process that will be debugged, so pausing in a breakpoint
+ //on managed code will freeze because it will not be able to continue executing the BrowserDebugProxy to get the locals value
+ var executablePath = Path.Combine(System.AppContext.BaseDirectory, "BrowserDebugHost.dll");
+ var ownerPid = Environment.ProcessId;
+ var generateRandomPort = new Random().Next(5000, 5300);
+ var processStartInfo = new ProcessStartInfo
+ {
+ FileName = "dotnet" + (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".exe" : ""),
+ Arguments = $"exec \"{executablePath}\" --OwnerPid {ownerPid} --DevToolsUrl {devToolsHost} --DevToolsProxyPort {generateRandomPort}",
+ UseShellExecute = false,
+ RedirectStandardOutput = true,
+ };
+ var debugProxyProcess = Process.Start(processStartInfo);
+ if (debugProxyProcess is null)
+ {
+ throw new InvalidOperationException("Unable to start debug proxy process.");
+ }
+ return generateRandomPort;
+ }
+
+ public void Configure(IApplicationBuilder app,
+ IOptions<WebServerOptions> optionsContainer,
+ TaskCompletionSource<ServerURLs> realUrlsAvailableTcs,
+ ILogger logger,
+ IHostApplicationLifetime applicationLifetime)
{
+ _logger = logger;
var provider = new FileExtensionContentTypeProvider();
provider.Mappings[".wasm"] = "application/wasm";
provider.Mappings[".cjs"] = "text/javascript";
@@ -73,9 +113,81 @@ internal sealed class WebServerStartup
});
}
- // app.UseEndpoints(endpoints =>
- // {
- // endpoints.MapFallbackToFile(options.DefaultFileName);
- // });
+ app.Map("/debug", app =>
+ {
+ app.Run(async (context) =>
+ {
+ //debug from VS
+ var queryParams = HttpUtility.ParseQueryString(context.Request.QueryString.Value!);
+ var browserParam = queryParams.Get("browser");
+ Uri? browserUrl = null;
+ var devToolsHost = "http://localhost:9222";
+ if (browserParam != null)
+ {
+ browserUrl = new Uri(browserParam);
+ devToolsHost = $"http://{browserUrl.Host}:{browserUrl.Port}";
+ }
+ lock (LaunchLock)
+ {
+ if (LaunchedDebugProxyUrl == "")
+ {
+ LaunchedDebugProxyUrl = $"http://localhost:{StartDebugProxy(devToolsHost)}";
+ }
+ }
+ var requestPath = context.Request.Path.ToString();
+ if (requestPath == string.Empty)
+ {
+ requestPath = "/";
+ }
+ context.Response.Redirect($"{LaunchedDebugProxyUrl}{browserUrl!.PathAndQuery}");
+ await Task.FromResult(0);
+ });
+ });
+ app.UseEndpoints(endpoints =>
+ {
+ endpoints.MapGet("/", context =>
+ {
+ context.Response.Redirect("index.html", permanent: false);
+ return Task.CompletedTask;
+ });
+ });
+
+
+ applicationLifetime.ApplicationStarted.Register(() =>
+ {
+ TaskCompletionSource<ServerURLs> tcs = realUrlsAvailableTcs;
+ try
+ {
+ ICollection<string>? addresses = app.ServerFeatures
+ .Get<IServerAddressesFeature>()
+ ?.Addresses;
+
+ string? ipAddress = null;
+ string? ipAddressSecure = null;
+ if (addresses is not null)
+ {
+ ipAddress = GetHttpServerAddress(addresses, secure: false);
+ ipAddressSecure = GetHttpServerAddress(addresses, secure: true);
+ }
+
+ if (ipAddress == null)
+ tcs.SetException(new InvalidOperationException("Failed to determine web server's IP address or port"));
+ else
+ tcs.SetResult(new ServerURLs(ipAddress, ipAddressSecure));
+ }
+ catch (Exception ex)
+ {
+ _logger?.LogError($"Failed to get urls for the webserver: {ex}");
+ tcs.TrySetException(ex);
+ throw;
+ }
+
+ static string? GetHttpServerAddress(ICollection<string> addresses, bool secure)
+ => addresses?
+ .Where(a => a.StartsWith(secure ? "https:" : "http:", StringComparison.InvariantCultureIgnoreCase))
+ .Select(a => new Uri(a))
+ .Select(uri => uri.ToString())
+ .FirstOrDefault();
+ });
}
}
diff --git a/src/mono/wasm/runtime/corebindings.c b/src/mono/wasm/runtime/corebindings.c
index c9ebdc01abd..d1b5a9e6842 100644
--- a/src/mono/wasm/runtime/corebindings.c
+++ b/src/mono/wasm/runtime/corebindings.c
@@ -35,7 +35,7 @@ extern void mono_wasm_bind_cs_function(MonoString **fully_qualified_name, int si
extern void mono_wasm_marshal_promise(void *data);
-void core_initialize_internals ()
+void core_initialize_internals (void)
{
mono_add_internal_call ("Interop/Runtime::InvokeJSWithArgsRef", mono_wasm_invoke_js_with_args_ref);
mono_add_internal_call ("Interop/Runtime::GetObjectPropertyRef", mono_wasm_get_object_property_ref);
diff --git a/src/mono/wasm/runtime/driver.c b/src/mono/wasm/runtime/driver.c
index e2db123f7ab..b14e341f6c8 100644
--- a/src/mono/wasm/runtime/driver.c
+++ b/src/mono/wasm/runtime/driver.c
@@ -452,7 +452,7 @@ get_native_to_interp (MonoMethod *method, void *extra_arg)
typedef void (*background_job_cb)(void);
void mono_threads_schedule_background_job (background_job_cb cb);
-void mono_initialize_internals ()
+void mono_initialize_internals (void)
{
// Blazor specific custom routines - see dotnet_support.js for backing code
mono_add_internal_call ("WebAssembly.JSInterop.InternalCalls::InvokeJS", mono_wasm_invoke_js_blazor);
@@ -465,7 +465,7 @@ void mono_initialize_internals ()
}
EMSCRIPTEN_KEEPALIVE void
-mono_wasm_register_bundled_satellite_assemblies ()
+mono_wasm_register_bundled_satellite_assemblies (void)
{
/* In legacy satellite_assembly_count is always false */
if (satellite_assembly_count) {
@@ -641,7 +641,7 @@ mono_wasm_assembly_load (const char *name)
}
EMSCRIPTEN_KEEPALIVE MonoAssembly*
-mono_wasm_get_corlib ()
+mono_wasm_get_corlib (void)
{
MonoAssembly* result;
MONO_ENTER_GC_UNSAFE;
@@ -912,7 +912,7 @@ _get_uri_class(MonoException** exc)
}
static void
-_ensure_classes_resolved ()
+_ensure_classes_resolved (void)
{
MONO_ENTER_GC_UNSAFE;
if (!datetime_class && !resolved_datetime_class) {
diff --git a/src/mono/wasm/runtime/logging.ts b/src/mono/wasm/runtime/logging.ts
index 2d40d7c4d20..cdb2d00dba7 100644
--- a/src/mono/wasm/runtime/logging.ts
+++ b/src/mono/wasm/runtime/logging.ts
@@ -62,8 +62,9 @@ export function mono_wasm_symbolicate_string(message: string): string {
export function mono_wasm_stringify_as_error_with_stack(err: Error | string): string {
let errObj: any = err;
- if (!(err instanceof Error))
- errObj = new Error(err);
+ if (!(errObj instanceof Error)) {
+ errObj = new Error(errObj);
+ }
// Error
return mono_wasm_symbolicate_string(errObj.stack);
diff --git a/src/mono/wasm/runtime/managed-exports.ts b/src/mono/wasm/runtime/managed-exports.ts
index 09f6d9a33d0..65186e4c0b2 100644
--- a/src/mono/wasm/runtime/managed-exports.ts
+++ b/src/mono/wasm/runtime/managed-exports.ts
@@ -7,7 +7,7 @@ import { Module, runtimeHelpers, ENVIRONMENT_IS_PTHREAD } from "./imports";
import { alloc_stack_frame, get_arg, get_arg_gc_handle, MarshalerType, set_arg_type, set_gc_handle } from "./marshal";
import { invoke_method_and_handle_exception } from "./invoke-cs";
import { marshal_array_to_cs_impl, marshal_exception_to_cs, marshal_intptr_to_cs } from "./marshal-to-cs";
-import { marshal_int32_to_js, marshal_task_to_js } from "./marshal-to-js";
+import { marshal_int32_to_js, marshal_string_to_js, marshal_task_to_js } from "./marshal-to-js";
export function init_managed_exports(): void {
const anyModule = Module as any;
@@ -34,6 +34,9 @@ export function init_managed_exports(): void {
mono_assert(complete_task_method, "Can't find CompleteTask method");
const call_delegate_method = get_method("CallDelegate");
mono_assert(call_delegate_method, "Can't find CallDelegate method");
+ const get_managed_stack_trace_method = get_method("GetManagedStackTrace");
+ mono_assert(get_managed_stack_trace_method, "Can't find GetManagedStackTrace method");
+
runtimeHelpers.javaScriptExports.call_entry_point = (entry_point: MonoMethod, program_args?: string[]) => {
const sp = anyModule.stackSave();
try {
@@ -134,6 +137,22 @@ export function init_managed_exports(): void {
anyModule.stackRestore(sp);
}
};
+ runtimeHelpers.javaScriptExports.get_managed_stack_trace = (exception_gc_handle: GCHandle) => {
+ const sp = anyModule.stackSave();
+ try {
+ const args = alloc_stack_frame(3);
+
+ const arg1 = get_arg(args, 2);
+ set_arg_type(arg1, MarshalerType.Exception);
+ set_gc_handle(arg1, exception_gc_handle);
+
+ invoke_method_and_handle_exception(get_managed_stack_trace_method, args);
+ const res = get_arg(args, 1);
+ return marshal_string_to_js(res);
+ } finally {
+ anyModule.stackRestore(sp);
+ }
+ };
if (install_sync_context) {
runtimeHelpers.javaScriptExports.install_synchronization_context = () => {
diff --git a/src/mono/wasm/runtime/marshal-to-js.ts b/src/mono/wasm/runtime/marshal-to-js.ts
index b3f469fc05f..a26de6dc147 100644
--- a/src/mono/wasm/runtime/marshal-to-js.ts
+++ b/src/mono/wasm/runtime/marshal-to-js.ts
@@ -33,7 +33,7 @@ export function initialize_marshalers_to_js(): void {
cs_to_js_marshalers.set(MarshalerType.Single, _marshal_float_to_js);
cs_to_js_marshalers.set(MarshalerType.IntPtr, _marshal_intptr_to_js);
cs_to_js_marshalers.set(MarshalerType.Double, _marshal_double_to_js);
- cs_to_js_marshalers.set(MarshalerType.String, _marshal_string_to_js);
+ cs_to_js_marshalers.set(MarshalerType.String, marshal_string_to_js);
cs_to_js_marshalers.set(MarshalerType.Exception, marshal_exception_to_js);
cs_to_js_marshalers.set(MarshalerType.JSException, marshal_exception_to_js);
cs_to_js_marshalers.set(MarshalerType.JSObject, _marshal_js_object_to_js);
@@ -296,7 +296,7 @@ export function mono_wasm_marshal_promise(args: JSMarshalerArguments): void {
set_arg_type(exc, MarshalerType.None);
}
-function _marshal_string_to_js(arg: JSMarshalerArgument): string | null {
+export function marshal_string_to_js(arg: JSMarshalerArgument): string | null {
const type = get_arg_type(arg);
if (type == MarshalerType.None) {
return null;
@@ -326,7 +326,7 @@ export function marshal_exception_to_js(arg: JSMarshalerArgument): Error | null
let result = _lookup_js_owned_object(gc_handle);
if (result === null || result === undefined) {
// this will create new ManagedError
- const message = _marshal_string_to_js(arg);
+ const message = marshal_string_to_js(arg);
result = new ManagedError(message!);
setup_managed_proxy(result, gc_handle);
@@ -405,7 +405,7 @@ function _marshal_array_to_js_impl(arg: JSMarshalerArgument, element_type: Marsh
result = new Array(length);
for (let index = 0; index < length; index++) {
const element_arg = get_arg(<any>buffer_ptr, index);
- result[index] = _marshal_string_to_js(element_arg);
+ result[index] = marshal_string_to_js(element_arg);
}
cwraps.mono_wasm_deregister_root(<any>buffer_ptr);
}
diff --git a/src/mono/wasm/runtime/marshal.ts b/src/mono/wasm/runtime/marshal.ts
index 591f3976292..c1e7e35535d 100644
--- a/src/mono/wasm/runtime/marshal.ts
+++ b/src/mono/wasm/runtime/marshal.ts
@@ -2,7 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
import { js_owned_gc_handle_symbol, teardown_managed_proxy } from "./gc-handles";
-import { Module } from "./imports";
+import { Module, runtimeHelpers } from "./imports";
import { getF32, getF64, getI16, getI32, getI64Big, getU16, getU32, getU8, setF32, setF64, setI16, setI32, setI64Big, setU16, setU32, setU8 } from "./memory";
import { mono_wasm_new_external_root } from "./roots";
import { mono_assert, GCHandle, JSHandle, MonoObject, MonoString, GCHandleNull, JSMarshalerArguments, JSFunctionSignature, JSMarshalerType, JSMarshalerArgument, MarshalerToJs, MarshalerToCs, WasmRoot } from "./types";
@@ -317,13 +317,31 @@ export class ManagedObject implements IDisposable {
}
export class ManagedError extends Error implements IDisposable {
+ private superStack: any;
constructor(message: string) {
super(message);
+ this.superStack = Object.getOwnPropertyDescriptor(this, "stack"); // this works on Chrome
+ Object.defineProperty(this, "stack", {
+ get: this.getManageStack,
+ });
}
- get stack(): string | undefined {
- //todo implement lazy managed stack strace from this[js_owned_gc_handle_symbol]!
- return super.stack;
+ getSuperStack() {
+ if (this.superStack) {
+ return this.superStack.value;
+ }
+ return super.stack; // this works on FF
+ }
+
+ getManageStack() {
+ const gc_handle = (<any>this)[js_owned_gc_handle_symbol];
+ if (gc_handle) {
+ const managed_stack = runtimeHelpers.javaScriptExports.get_managed_stack_trace(gc_handle);
+ if (managed_stack) {
+ return managed_stack + "\n" + this.getSuperStack();
+ }
+ }
+ return this.getSuperStack();
}
dispose(): void {
@@ -333,10 +351,6 @@ export class ManagedError extends Error implements IDisposable {
get isDisposed(): boolean {
return (<any>this)[js_owned_gc_handle_symbol] === GCHandleNull;
}
-
- toString(): string {
- return `ManagedError(gc_handle: ${(<any>this)[js_owned_gc_handle_symbol]})`;
- }
}
export function get_signature_marshaler(signature: JSFunctionSignature, index: number): JSHandle {
diff --git a/src/mono/wasm/runtime/types.ts b/src/mono/wasm/runtime/types.ts
index ba56005ef2d..ca914585d1d 100644
--- a/src/mono/wasm/runtime/types.ts
+++ b/src/mono/wasm/runtime/types.ts
@@ -414,6 +414,9 @@ export interface JavaScriptExports {
// the marshaled signature is: void InstallSynchronizationContext()
install_synchronization_context(): void;
+
+ // the marshaled signature is: string GetManagedStackTrace(GCHandle exception)
+ get_managed_stack_trace(exception_gc_handle: GCHandle): string | null
}
export type MarshalerToJs = (arg: JSMarshalerArgument, sig?: JSMarshalerType, res_converter?: MarshalerToJs, arg1_converter?: MarshalerToCs, arg2_converter?: MarshalerToCs, arg3_converter?: MarshalerToCs) => any;
diff --git a/src/mono/wasm/templates/templates/browser/.template.config/template.json b/src/mono/wasm/templates/templates/browser/.template.config/template.json
index 8051e4c6aab..7fc4f155519 100644
--- a/src/mono/wasm/templates/templates/browser/.template.config/template.json
+++ b/src/mono/wasm/templates/templates/browser/.template.config/template.json
@@ -2,13 +2,52 @@
"$schema": "http://json.schemastore.org/template",
"author": "Microsoft",
"classifications": [ "Web", "WebAssembly", "Browser" ],
- "identity": "WebAssembly.Browser",
+ "generatorVersions": "[1.0.0.0-*)",
+ "groupIdentity": "WebAssembly.Browser",
+ "precedence": 8000,
+ "identity": "WebAssembly.Browser.8.0",
+ "description": "WebAssembly Browser App",
"name": "WebAssembly Browser App",
+ "description": "A project template for creating a .NET app that runs on WebAssembly in a browser",
"shortName": "wasmbrowser",
"sourceName": "browser.0",
"preferNameDirectory": true,
"tags": {
"language": "C#",
"type": "project"
+ },
+ "symbols": {
+ "kestrelHttpPortGenerated": {
+ "type": "generated",
+ "generator": "port",
+ "parameters": {
+ "low": 5000,
+ "high": 5300
+ },
+ "replaces": "5000"
+ },
+ "kestrelHttpsPortGenerated": {
+ "type": "generated",
+ "generator": "port",
+ "parameters": {
+ "low": 7000,
+ "high": 7300
+ },
+ "replaces": "5001"
+ },
+ "framework": {
+ "type": "parameter",
+ "description": "The target framework for the project.",
+ "datatype": "choice",
+ "choices": [
+ {
+ "choice": "net8.0",
+ "description": "Target net8.0",
+ "displayName": ".NET 8.0"
+ }
+ ],
+ "defaultValue": "net8.0",
+ "displayName": "framework"
+ }
}
}
diff --git a/src/mono/wasm/templates/templates/browser/Properties/launchSettings.json b/src/mono/wasm/templates/templates/browser/Properties/launchSettings.json
new file mode 100644
index 00000000000..4807594b8f5
--- /dev/null
+++ b/src/mono/wasm/templates/templates/browser/Properties/launchSettings.json
@@ -0,0 +1,13 @@
+{
+ "profiles": {
+ "browser.0": {
+ "commandName": "Project",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "applicationUrl": "https://localhost:5001;http://localhost:5000",
+ "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/debug?browser={browserInspectUri}"
+ }
+ }
+}
diff --git a/src/mono/wasm/templates/templates/browser/main.js b/src/mono/wasm/templates/templates/browser/main.js
index 32c1599749d..6d9bd43f7eb 100644
--- a/src/mono/wasm/templates/templates/browser/main.js
+++ b/src/mono/wasm/templates/templates/browser/main.js
@@ -3,15 +3,12 @@
import { dotnet } from './dotnet.js'
-const is_browser = typeof window != "undefined";
-if (!is_browser) throw new Error(`Expected to be running in a browser`);
-
-const { setModuleImports, getAssemblyExports, getConfig, runMainAndExit } = await dotnet
+const { setModuleImports, getAssemblyExports, getConfig } = await dotnet
.withDiagnosticTracing(false)
.withApplicationArgumentsFromQuery()
.create();
-setModuleImports("main.js", {
+setModuleImports('main.js', {
window: {
location: {
href: () => globalThis.window.location.href
@@ -24,5 +21,5 @@ const exports = await getAssemblyExports(config.mainAssemblyName);
const text = exports.MyClass.Greeting();
console.log(text);
-document.getElementById("out").innerHTML = `${text}`;
-await runMainAndExit(config.mainAssemblyName, ["dotnet", "is", "great!"]); \ No newline at end of file
+document.getElementById('out').innerHTML = text;
+await dotnet.run(); \ No newline at end of file
diff --git a/src/mono/wasm/templates/templates/console/.template.config/template.json b/src/mono/wasm/templates/templates/console/.template.config/template.json
index 8ead39edc0f..0ea523824ae 100644
--- a/src/mono/wasm/templates/templates/console/.template.config/template.json
+++ b/src/mono/wasm/templates/templates/console/.template.config/template.json
@@ -2,13 +2,33 @@
"$schema": "http://json.schemastore.org/template",
"author": "Microsoft",
"classifications": [ "Web", "WebAssembly", "Console" ],
- "identity": "WebAssembly.Console",
+ "groupIdentity": "WebAssembly.Console",
+ "precedence": 8000,
+ "identity": "WebAssembly.Console.8.0",
+ "description": "WebAssembly Console App",
"name": "WebAssembly Console App",
+ "description": "A project template for creating a .NET app that runs on WebAssembly on Node JS or V8",
"shortName": "wasmconsole",
"sourceName": "console.0",
"preferNameDirectory": true,
"tags": {
"language": "C#",
"type": "project"
+ },
+ "symbols": {
+ "framework": {
+ "type": "parameter",
+ "description": "The target framework for the project.",
+ "datatype": "choice",
+ "choices": [
+ {
+ "choice": "net8.0",
+ "description": "Target net8.0",
+ "displayName": ".NET 8.0"
+ }
+ ],
+ "defaultValue": "net8.0",
+ "displayName": "framework"
+ }
}
}
diff --git a/src/mono/wasm/templates/templates/console/main.mjs b/src/mono/wasm/templates/templates/console/main.mjs
index 1072711c59c..6dd163a3741 100644
--- a/src/mono/wasm/templates/templates/console/main.mjs
+++ b/src/mono/wasm/templates/templates/console/main.mjs
@@ -3,14 +3,11 @@
import { dotnet } from './dotnet.js'
-const is_node = typeof process === 'object' && typeof process.versions === 'object' && typeof process.versions.node === 'string';
-if (!is_node) throw new Error(`This file only supports nodejs`);
-
-const { setModuleImports, getAssemblyExports, getConfig, runMainAndExit } = await dotnet
+const { setModuleImports, getAssemblyExports, getConfig } = await dotnet
.withDiagnosticTracing(false)
.create();
-setModuleImports("main.mjs", {
+setModuleImports('main.mjs', {
node: {
process: {
version: () => globalThis.process.version
@@ -23,4 +20,4 @@ const exports = await getAssemblyExports(config.mainAssemblyName);
const text = exports.MyClass.Greeting();
console.log(text);
-await runMainAndExit(config.mainAssemblyName, ["dotnet", "is", "great!"]);
+await dotnet.run();
diff --git a/src/mono/wasm/wasm.proj b/src/mono/wasm/wasm.proj
index cac849c5818..1a8fd0bf0fa 100644
--- a/src/mono/wasm/wasm.proj
+++ b/src/mono/wasm/wasm.proj
@@ -332,6 +332,9 @@
<!-- npm install is faster on dev machine as it doesn't wipe node_modules folder -->
<RunWithEmSdkEnv Condition="'$(ContinuousIntegrationBuild)' != 'true'" Command="npm install" EmSdkPath="$(EMSDK_PATH)" IgnoreStandardErrorWarningFormat="true" WorkingDirectory="$(MonoProjectRoot)wasm/runtime/"/>
+ <!-- Delete malformed package.json used for tests, it confuses Component Governance tooling -->
+ <Delete Files="$(MonoProjectRoot)wasm/runtime/node_modules/resolve/test/resolver/malformed_package_json/package.json" ContinueOnError="true" />
+
<Touch Files="$(MonoProjectRoot)wasm/runtime/node_modules/.npm-stamp" AlwaysCreate="true" />
</Target>