From 2e3e3317406c835101acd33a683d23030c0b2e87 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 15 Sep 2022 13:20:11 -0700 Subject: [PATCH 01/15] [release/7.0-rc2] Use determinism instead of timing in cancellation tests (#75655) * Use determinism instead of timing in cancellation tests * Add an explanatory comment to the test. * Spell "canceling" with only one L. Co-authored-by: Jeremy Barton --- .../tests/HashAlgorithmTest.cs | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.Security.Cryptography/tests/HashAlgorithmTest.cs b/src/libraries/System.Security.Cryptography/tests/HashAlgorithmTest.cs index 015f36c8a979f7..acd7df3a9f8077 100644 --- a/src/libraries/System.Security.Cryptography/tests/HashAlgorithmTest.cs +++ b/src/libraries/System.Security.Cryptography/tests/HashAlgorithmTest.cs @@ -62,19 +62,27 @@ public async Task VerifyComputeHashAsync(int size) [ActiveIssue("https://github.com/dotnet/runtime/issues/37669", TestPlatforms.Browser)] public async Task ComputeHashAsync_SupportsCancellation() { - using (CancellationTokenSource cancellationSource = new CancellationTokenSource(100)) - using (PositionValueStream stream = new SlowPositionValueStream(10000)) + using (CancellationTokenSource cancellationSource = new CancellationTokenSource()) + using (PositionValueStream stream = new SelfCancelingStream(10000, cancellationSource)) using (HashAlgorithm hash = new SummingTestHashAlgorithm()) { + // The stream has a length longer than ComputeHashAsync's read buffer, + // so ReadAsync will get called multiple times. + // The first call succeeds, but moves the cancellation source to canceled, + // and the second call then fails with an OperationCanceledException, canceling the + // whole operation. await Assert.ThrowsAnyAsync( () => hash.ComputeHashAsync(stream, cancellationSource.Token)); + + Assert.True(cancellationSource.IsCancellationRequested); } } [Fact] public void ComputeHashAsync_Disposed() { - using (PositionValueStream stream = new SlowPositionValueStream(10000)) + using (CancellationTokenSource cancellationSource = new CancellationTokenSource()) + using (PositionValueStream stream = new SelfCancelingStream(10000, cancellationSource)) using (HashAlgorithm hash = new SummingTestHashAlgorithm()) { hash.Dispose(); @@ -85,6 +93,10 @@ public void ComputeHashAsync_Disposed() // Not returning or awaiting the Task, it never got created. hash.ComputeHashAsync(stream); }); + + // If SelfCancelingStream.Read (or ReadAsync) was called it will trip cancellation, + // so use that as a signal for whether or not the stream was ever read from. + Assert.False(cancellationSource.IsCancellationRequested, "Stream.Read was invoked"); } } @@ -123,15 +135,19 @@ protected override void HashCore(byte[] array, int ibStart, int cbSize) // implementations by verifying the right value is produced. } - private class SlowPositionValueStream : PositionValueStream + private class SelfCancelingStream : PositionValueStream { - public SlowPositionValueStream(int totalCount) : base(totalCount) + private readonly CancellationTokenSource _cancellationSource; + + public SelfCancelingStream(int totalCount, CancellationTokenSource cancellationSource) + : base(totalCount) { + _cancellationSource = cancellationSource; } public override int Read(byte[] buffer, int offset, int count) { - System.Threading.Thread.Sleep(1000); + _cancellationSource.Cancel(throwOnFirstException: true); return base.Read(buffer, offset, count); } } From 393e1abe93618ee1f4633e4087f0fc311f2b21f2 Mon Sep 17 00:00:00 2001 From: Carlos Sanchez <1175054+carlossanlop@users.noreply.github.com> Date: Thu, 15 Sep 2022 14:21:12 -0700 Subject: [PATCH 02/15] Bump Intellisense RC2 package version again (#75698) --- eng/Versions.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/Versions.props b/eng/Versions.props index bb3c6456d69106..7d7b50789a5cc0 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -174,7 +174,7 @@ 1.1.2-beta1.22403.2 - 7.0.0-preview-20220914.1 + 7.0.0-preview-20220915.1 7.0.100-1.22423.4 $(MicrosoftNETILLinkTasksVersion) From f0eb5cee40bdb78dec3f460aaa977b16520359fb Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Fri, 16 Sep 2022 19:21:33 +0300 Subject: [PATCH 03/15] Implement an AppContext compatibility switch re-enabling reflection fallback in STJ source generators. (#75615) (#75694) * Implement an AppContext compatibility switch re-enabling reflection fallback in sourcegen. * address feedback --- .../src/System.Text.Json.csproj | 1 + .../Text/Json/AppContextSwitchHelper.cs | 16 +++++++ .../Serialization/JsonSerializerOptions.cs | 20 +++++++- .../Serialization/OptionsTests.cs | 48 +++++++++++++++++-- 4 files changed, 80 insertions(+), 5 deletions(-) create mode 100644 src/libraries/System.Text.Json/src/System/Text/Json/AppContextSwitchHelper.cs diff --git a/src/libraries/System.Text.Json/src/System.Text.Json.csproj b/src/libraries/System.Text.Json/src/System.Text.Json.csproj index e17ce01c770357..8cd779f5a02188 100644 --- a/src/libraries/System.Text.Json/src/System.Text.Json.csproj +++ b/src/libraries/System.Text.Json/src/System.Text.Json.csproj @@ -36,6 +36,7 @@ The System.Text.Json library is built-in as part of the shared framework in .NET + diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/AppContextSwitchHelper.cs b/src/libraries/System.Text.Json/src/System/Text/Json/AppContextSwitchHelper.cs new file mode 100644 index 00000000000000..9c028f02165171 --- /dev/null +++ b/src/libraries/System.Text.Json/src/System/Text/Json/AppContextSwitchHelper.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Text.Json +{ + internal static class AppContextSwitchHelper + { + public static bool IsSourceGenReflectionFallbackEnabled => s_isSourceGenReflectionFallbackEnabled; + + private static readonly bool s_isSourceGenReflectionFallbackEnabled = + AppContext.TryGetSwitch( + switchName: "System.Text.Json.Serialization.EnableSourceGenReflectionFallback", + isEnabled: out bool value) + ? value : false; + } +} diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs index 596a9c37bae488..fa543671792fb0 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs @@ -628,7 +628,20 @@ internal void InitializeForReflectionSerializer() // Even if a resolver has already been specified, we need to root // the default resolver to gain access to the default converters. DefaultJsonTypeInfoResolver defaultResolver = DefaultJsonTypeInfoResolver.RootDefaultInstance(); - _typeInfoResolver ??= defaultResolver; + + switch (_typeInfoResolver) + { + case null: + // Use the default reflection-based resolver if no resolver has been specified. + _typeInfoResolver = defaultResolver; + break; + + case JsonSerializerContext ctx when AppContextSwitchHelper.IsSourceGenReflectionFallbackEnabled: + // .NET 6 compatibility mode: enable fallback to reflection metadata for JsonSerializerContext + _effectiveJsonTypeInfoResolver = JsonTypeInfoResolver.Combine(ctx, defaultResolver); + break; + } + IsImmutable = true; _isInitializedForReflectionSerializer = true; } @@ -636,6 +649,9 @@ internal void InitializeForReflectionSerializer() internal bool IsInitializedForReflectionSerializer => _isInitializedForReflectionSerializer; private volatile bool _isInitializedForReflectionSerializer; + // Only populated in .NET 6 compatibility mode encoding reflection fallback in source gen + private IJsonTypeInfoResolver? _effectiveJsonTypeInfoResolver; + internal void InitializeForMetadataGeneration() { if (_typeInfoResolver is null) @@ -648,7 +664,7 @@ internal void InitializeForMetadataGeneration() private JsonTypeInfo? GetTypeInfoNoCaching(Type type) { - JsonTypeInfo? info = _typeInfoResolver?.GetTypeInfo(type, this); + JsonTypeInfo? info = (_effectiveJsonTypeInfoResolver ?? _typeInfoResolver)?.GetTypeInfo(type, this); if (info != null) { diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/OptionsTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/OptionsTests.cs index a91467a9943a3e..05cd443bccf5ac 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/OptionsTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/OptionsTests.cs @@ -437,9 +437,18 @@ public static void Options_JsonSerializerContext_DoesNotFallbackToReflection() } [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public static void Options_JsonSerializerContext_GetConverter_DoesNotFallBackToReflectionConverter() + [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [InlineData(false)] + [InlineData(true)] + public static void Options_JsonSerializerContext_GetConverter_DoesNotFallBackToReflectionConverter(bool isCompatibilitySwitchExplicitlyDisabled) { + var options = new RemoteInvokeOptions(); + + if (isCompatibilitySwitchExplicitlyDisabled) + { + options.RuntimeConfigurationOptions.Add("System.Text.Json.Serialization.EnableSourceGenReflectionFallback", false); + } + RemoteExecutor.Invoke(static () => { JsonContext context = JsonContext.Default; @@ -460,7 +469,40 @@ public static void Options_JsonSerializerContext_GetConverter_DoesNotFallBackToR Assert.Throws(() => context.Options.GetConverter(typeof(MyClass))); Assert.Throws(() => JsonSerializer.Serialize(unsupportedValue, context.Options)); - }).Dispose(); + }, options).Dispose(); + } + + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public static void Options_JsonSerializerContext_Net6CompatibilitySwitch_FallsBackToReflectionResolver() + { + var options = new RemoteInvokeOptions + { + RuntimeConfigurationOptions = + { + ["System.Text.Json.Serialization.EnableSourceGenReflectionFallback"] = true + } + }; + + RemoteExecutor.Invoke(static () => + { + var unsupportedValue = new MyClass { Value = "value" }; + + // JsonSerializerContext does not return metadata for the type + Assert.Null(JsonContext.Default.GetTypeInfo(typeof(MyClass))); + + // Serialization fails using the JsonSerializerContext overload + Assert.Throws(() => JsonSerializer.Serialize(unsupportedValue, unsupportedValue.GetType(), JsonContext.Default)); + + // Serialization uses reflection fallback using the JsonSerializerOptions overload + string json = JsonSerializer.Serialize(unsupportedValue, JsonContext.Default.Options); + JsonTestHelper.AssertJsonEqual("""{"Value":"value", "Thing":null}""", json); + + // A converter can be resolved when looking up JsonSerializerOptions + JsonConverter converter = JsonContext.Default.Options.GetConverter(typeof(MyClass)); + Assert.IsAssignableFrom>(converter); + + }, options).Dispose(); } [Fact] From fdca69896c73c5ee5ba9b1208a7396f1c2cff807 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 16 Sep 2022 09:26:21 -0700 Subject: [PATCH 04/15] [release/7.0-rc2] Handle a null szPname field (#75716) * Handle a null szPname field * Always clear the span to ensure we have a null terminator in case the string is short * Use null conditional access * Add call to test to test scenario * Update src/libraries/System.Speech/tests/SynthesizeRecognizeTests.cs Co-authored-by: Jeremy Koritzinsky Co-authored-by: Jeremy Koritzinsky --- .../src/Interop/Windows/WinMm/Interop.waveOutGetDevCaps.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libraries/Common/src/Interop/Windows/WinMm/Interop.waveOutGetDevCaps.cs b/src/libraries/Common/src/Interop/Windows/WinMm/Interop.waveOutGetDevCaps.cs index 3c3e831fac98d4..a9ebcba2950c55 100644 --- a/src/libraries/Common/src/Interop/Windows/WinMm/Interop.waveOutGetDevCaps.cs +++ b/src/libraries/Common/src/Interop/Windows/WinMm/Interop.waveOutGetDevCaps.cs @@ -50,7 +50,9 @@ public Native(WAVEOUTCAPS managed) wMid = managed.wMid; wPid = managed.wPid; vDriverVersion = managed.vDriverVersion; - managed.szPname.CopyTo(MemoryMarshal.CreateSpan(ref szPname[0], szPnameLength)); + Span szPnameSpan = MemoryMarshal.CreateSpan(ref szPname[0], szPnameLength); + szPnameSpan.Clear(); + managed.szPname?.CopyTo(szPnameSpan); dwFormats = managed.dwFormats; wChannels = managed.wChannels; wReserved1 = managed.wReserved1; From 6cf6e4874504b808f84d9bd48de5149502866f27 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 16 Sep 2022 09:41:19 -0700 Subject: [PATCH 05/15] [wasm] Use newer helix images with updated v8 (#75726) Co-authored-by: Radek Doulik --- eng/pipelines/libraries/helix-queues-setup.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eng/pipelines/libraries/helix-queues-setup.yml b/eng/pipelines/libraries/helix-queues-setup.yml index ca02a670d2142c..63253997dfc422 100644 --- a/eng/pipelines/libraries/helix-queues-setup.yml +++ b/eng/pipelines/libraries/helix-queues-setup.yml @@ -193,10 +193,10 @@ jobs: # WebAssembly Firefox - ${{ if eq(parameters.platform, 'Browser_wasm_firefox') }}: - - (Ubuntu.1804.Amd64)Ubuntu.1804.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-18.04-helix-webassembly-20220504035734-67908a0 + - (Ubuntu.1804.Amd64)Ubuntu.1804.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-18.04-helix-webassembly-20220908122752-67908a0 # WebAssembly windows - ${{ if eq(parameters.platform, 'Browser_wasm_win') }}: - - (Windows.Amd64.Server2022.Open)windows.amd64.server2022.open@mcr.microsoft.com/dotnet-buildtools/prereqs:windowsservercore-ltsc2022-helix-webassembly-20220620175048-bf70060 + - (Windows.Amd64.Server2022.Open)windows.amd64.server2022.open@mcr.microsoft.com/dotnet-buildtools/prereqs:windowsservercore-ltsc2022-helix-webassembly-20220908122953-3a6fb49 ${{ insert }}: ${{ parameters.jobParameters }} From e0a65e0713507223165891ba39a999763f31bfd4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 16 Sep 2022 09:48:25 -0700 Subject: [PATCH 06/15] Fix edge cases of getting attribute data from syntax (#75728) Fix two customer-discovered edge cases that were causing analyzer crashes. Fixes #75681 Fixes #75706 Co-authored-by: Jeremy Koritzinsky --- .../Analyzers/SyntaxExtensions.cs | 15 +++++++++++++-- .../NativeMarshallingAttributeAnalyzerTests.cs | 10 ++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/SyntaxExtensions.cs b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/SyntaxExtensions.cs index 20400c4721369e..3fea4b0fae2e21 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/SyntaxExtensions.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Analyzers/SyntaxExtensions.cs @@ -33,7 +33,17 @@ public static Location FindTypeExpressionOrNullLocation(this AttributeArgumentSy switch (attributeTarget.Identifier.Kind()) { case SyntaxKind.ReturnKeyword: - return ((IMethodSymbol)targetSymbol).GetReturnTypeAttributes().First(attributeSyntaxLocationMatches); + if (targetSymbol is IMethodSymbol method) + { + // Sometimes an attribute is put on a symbol that is nested within the containing symbol. + // For example, the ContainingSymbol for an AttributeSyntax on a local function have a ContainingSymbol of the method. + // Since this method is internal and the callers don't care about attributes on local functions, + // we just allow this method to return null in those cases. + return method.GetReturnTypeAttributes().FirstOrDefault(attributeSyntaxLocationMatches); + } + // An attribute on the return value of a delegate type's Invoke method has a ContainingSymbol of the delegate type. + // We don't care about the attributes in this case for the callers, so we'll just return null. + return null; case SyntaxKind.AssemblyKeyword: return targetSymbol.ContainingAssembly.GetAttributes().First(attributeSyntaxLocationMatches); case SyntaxKind.ModuleKeyword: @@ -43,7 +53,8 @@ public static Location FindTypeExpressionOrNullLocation(this AttributeArgumentSy } } // Sometimes an attribute is put on a symbol that is nested within the containing symbol. - // For example, the ContainingSymbol for an AttributeSyntax on a parameter have a ContainingSymbol of the method. + // For example, the ContainingSymbol for an AttributeSyntax on a parameter have a ContainingSymbol of the method + // and an AttributeSyntax on a local function have a ContainingSymbol of the containing method. // Since this method is internal and the callers don't care about attributes on parameters, we just allow // this method to return null in those cases. return targetSymbol.GetAttributes().FirstOrDefault(attributeSyntaxLocationMatches); diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/NativeMarshallingAttributeAnalyzerTests.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/NativeMarshallingAttributeAnalyzerTests.cs index 582568718722b5..5b8769e19c9ee2 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/NativeMarshallingAttributeAnalyzerTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/NativeMarshallingAttributeAnalyzerTests.cs @@ -272,8 +272,18 @@ public class X { void Foo([MarshalAs(UnmanagedType.I4)] int i) { + [return:MarshalAs(UnmanagedType.I4)] + [SkipLocalsInit] + static int Local() + { + return 0; + } } } + + [return:MarshalAs(UnmanagedType.I4)] + delegate int Y(); + """; await VerifyCS.VerifyAnalyzerAsync(source); From e869f4467522d4f85c3326116ba905572a4db594 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 16 Sep 2022 10:10:36 -0700 Subject: [PATCH 07/15] [release/7.0-rc2] Fix wasm template README (#75766) * Fix typo * Fix dotnet run arguments * double dashes might be understood to mean different things, so change to --h * remove RuntimeIdentifier settings and host settings from build/run argument Co-authored-by: yamachu --- src/mono/wasm/templates/templates/browser/README.md | 8 ++++---- src/mono/wasm/templates/templates/console/README.md | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/mono/wasm/templates/templates/browser/README.md b/src/mono/wasm/templates/templates/browser/README.md index f3c4ddce82ffc9..8e1339897bef46 100644 --- a/src/mono/wasm/templates/templates/browser/README.md +++ b/src/mono/wasm/templates/templates/browser/README.md @@ -2,20 +2,20 @@ ## Build -You can build the applcation from Visual Studio or by dotnet cli +You can build the application from Visual Studio or by dotnet cli ``` -dotnet build -c Debug/Release -r browser-wasm +dotnet build -c Debug/Release ``` After building the application, the result is in the `bin/$(Configuration)/net7.0/browser-wasm/AppBundle` directory. ## Run -You can build the applcation from Visual Studio or by dotnet cli +You can build the application from Visual Studio or by dotnet cli ``` -dotnet run -c Debug/Release -r browser-wasm +dotnet run -c Debug/Release ``` Or you can start any static file server from the AppBundle directory diff --git a/src/mono/wasm/templates/templates/console/README.md b/src/mono/wasm/templates/templates/console/README.md index a3b8dbc660e5d0..84fd14b27897e2 100644 --- a/src/mono/wasm/templates/templates/console/README.md +++ b/src/mono/wasm/templates/templates/console/README.md @@ -2,20 +2,20 @@ ## Build -You can build the applcation from Visual Studio or by dotnet cli +You can build the application from Visual Studio or by dotnet cli ``` -dotnet build -c Debug/Release -r browser-wasm +dotnet build -c Debug/Release ``` After building the application, the result is in the `bin/$(Configuration)/net7.0/browser-wasm/AppBundle` directory. ## Run -You can build the applcation from Visual Studio or by dotnet cli +You can build the application from Visual Studio or by dotnet cli ``` -dotnet run -c Debug/Release -r browser-wasm -h=nodejs +dotnet run -c Debug/Release ``` Or you can start any static file server from the AppBundle directory From b9d050a2b0400603ab48758a018d3b846be4575f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 16 Sep 2022 11:14:25 -0700 Subject: [PATCH 08/15] Fix Configuration.Binder for collection properties with no setters (#75723) Binding to an IDictionary/ICollection/ISet in ConfigurationBinder with no setter was failing because we were returning too early. Only returning early now if we were able to set the property, or if the interface is read-only. Fix #75626 Co-authored-by: Eric Erhardt --- .../src/ConfigurationBinder.cs | 46 ++++++++++++------- .../tests/ConfigurationBinderTests.cs | 23 ++++++++++ .../ConfigurationCollectionBindingTests.cs | 22 ++++++++- 3 files changed, 72 insertions(+), 19 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs index a26b2fe68e7190..e36ae28fd4316d 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs @@ -299,43 +299,45 @@ private static void BindInstance( if (config != null && config.GetChildren().Any()) { - // for arrays, collections, and read-only list-like interfaces, we concatenate on to what is already there + // for arrays, collections, and read-only list-like interfaces, we concatenate on to what is already there, if we can if (type.IsArray || IsArrayCompatibleInterface(type)) { if (!bindingPoint.IsReadOnly) { bindingPoint.SetValue(BindArray(type, (IEnumerable?)bindingPoint.Value, config, options)); + return; + } + + // for getter-only collection properties that we can't add to, nothing more we can do + if (type.IsArray || IsImmutableArrayCompatibleInterface(type)) + { + return; } - return; } - // for sets and read-only set interfaces, we clone what's there into a new collection. - if (TypeIsASetInterface(type)) + // for sets and read-only set interfaces, we clone what's there into a new collection, if we can + if (TypeIsASetInterface(type) && !bindingPoint.IsReadOnly) { - if (!bindingPoint.IsReadOnly) + object? newValue = BindSet(type, (IEnumerable?)bindingPoint.Value, config, options); + if (newValue != null) { - object? newValue = BindSet(type, (IEnumerable?)bindingPoint.Value, config, options); - if (newValue != null) - { - bindingPoint.SetValue(newValue); - } + bindingPoint.SetValue(newValue); } + return; } // For other mutable interfaces like ICollection<>, IDictionary<,> and ISet<>, we prefer copying values and setting them // on a new instance of the interface over populating the existing instance implementing the interface. // This has already been done, so there's not need to check again. - if (TypeIsADictionaryInterface(type)) + if (TypeIsADictionaryInterface(type) && !bindingPoint.IsReadOnly) { - if (!bindingPoint.IsReadOnly) + object? newValue = BindDictionaryInterface(bindingPoint.Value, type, config, options); + if (newValue != null) { - object? newValue = BindDictionaryInterface(bindingPoint.Value, type, config, options); - if (newValue != null) - { - bindingPoint.SetValue(newValue); - } + bindingPoint.SetValue(newValue); } + return; } @@ -848,6 +850,16 @@ private static bool IsArrayCompatibleInterface(Type type) || genericTypeDefinition == typeof(IReadOnlyList<>); } + private static bool IsImmutableArrayCompatibleInterface(Type type) + { + if (!type.IsInterface || !type.IsConstructedGenericType) { return false; } + + Type genericTypeDefinition = type.GetGenericTypeDefinition(); + return genericTypeDefinition == typeof(IEnumerable<>) + || genericTypeDefinition == typeof(IReadOnlyCollection<>) + || genericTypeDefinition == typeof(IReadOnlyList<>); + } + private static bool TypeIsASetInterface(Type type) { if (!type.IsInterface || !type.IsConstructedGenericType) { return false; } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/ConfigurationBinderTests.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/ConfigurationBinderTests.cs index 9eae194f02cf99..521501d5938de3 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/ConfigurationBinderTests.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/ConfigurationBinderTests.cs @@ -72,6 +72,8 @@ public string ReadOnly public ISet InstantiatedISet { get; set; } = new HashSet(); + public ISet ISetNoSetter { get; } = new HashSet(); + public HashSet InstantiatedHashSetWithSomeValues { get; set; } = new HashSet(new[] {"existing1", "existing2"}); @@ -662,6 +664,27 @@ public void CanBindNonInstantiatedISet() Assert.Equal("Yo2", options.NonInstantiatedISet.ElementAt(1)); } + [Fact] + public void CanBindISetNoSetter() + { + var dic = new Dictionary + { + {"ISetNoSetter:0", "Yo1"}, + {"ISetNoSetter:1", "Yo2"}, + {"ISetNoSetter:2", "Yo2"}, + }; + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddInMemoryCollection(dic); + + var config = configurationBuilder.Build(); + + var options = config.Get()!; + + Assert.Equal(2, options.ISetNoSetter.Count); + Assert.Equal("Yo1", options.ISetNoSetter.ElementAt(0)); + Assert.Equal("Yo2", options.ISetNoSetter.ElementAt(1)); + } + #if NETCOREAPP [Fact] public void CanBindInstantiatedIReadOnlySet() diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/ConfigurationCollectionBindingTests.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/ConfigurationCollectionBindingTests.cs index 7367d0664cf356..4f2b5911b2b7a9 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/ConfigurationCollectionBindingTests.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/ConfigurationCollectionBindingTests.cs @@ -579,7 +579,10 @@ public void AlreadyInitializedStringDictionaryBinding() { {"AlreadyInitializedStringDictionaryInterface:abc", "val_1"}, {"AlreadyInitializedStringDictionaryInterface:def", "val_2"}, - {"AlreadyInitializedStringDictionaryInterface:ghi", "val_3"} + {"AlreadyInitializedStringDictionaryInterface:ghi", "val_3"}, + + {"IDictionaryNoSetter:Key1", "Value1"}, + {"IDictionaryNoSetter:Key2", "Value2"}, }; var configurationBuilder = new ConfigurationBuilder(); @@ -596,6 +599,10 @@ public void AlreadyInitializedStringDictionaryBinding() Assert.Equal("val_1", options.AlreadyInitializedStringDictionaryInterface["abc"]); Assert.Equal("val_2", options.AlreadyInitializedStringDictionaryInterface["def"]); Assert.Equal("val_3", options.AlreadyInitializedStringDictionaryInterface["ghi"]); + + Assert.Equal(2, options.IDictionaryNoSetter.Count); + Assert.Equal("Value1", options.IDictionaryNoSetter["Key1"]); + Assert.Equal("Value2", options.IDictionaryNoSetter["Key2"]); } [Fact] @@ -1059,7 +1066,10 @@ public void CanBindInitializedIEnumerableAndTheOriginalItemsAreNotMutated() {"AlreadyInitializedIEnumerableInterface:0", "val0"}, {"AlreadyInitializedIEnumerableInterface:1", "val1"}, {"AlreadyInitializedIEnumerableInterface:2", "val2"}, - {"AlreadyInitializedIEnumerableInterface:x", "valx"} + {"AlreadyInitializedIEnumerableInterface:x", "valx"}, + + {"ICollectionNoSetter:0", "val0"}, + {"ICollectionNoSetter:1", "val1"}, }; var configurationBuilder = new ConfigurationBuilder(); @@ -1084,6 +1094,10 @@ public void CanBindInitializedIEnumerableAndTheOriginalItemsAreNotMutated() Assert.Equal(2, options.ListUsedInIEnumerableFieldAndShouldNotBeTouched.Count); Assert.Equal("This was here too", options.ListUsedInIEnumerableFieldAndShouldNotBeTouched.ElementAt(0)); Assert.Equal("Don't touch me!", options.ListUsedInIEnumerableFieldAndShouldNotBeTouched.ElementAt(1)); + + Assert.Equal(2, options.ICollectionNoSetter.Count); + Assert.Equal("val0", options.ICollectionNoSetter.ElementAt(0)); + Assert.Equal("val1", options.ICollectionNoSetter.ElementAt(1)); } [Fact] @@ -1424,6 +1438,8 @@ public InitializedCollectionsOptions() new CustomListIndirectlyDerivedFromIEnumerable(); public IReadOnlyDictionary AlreadyInitializedDictionary { get; set; } + + public ICollection ICollectionNoSetter { get; } = new List(); } private class CustomList : List @@ -1564,6 +1580,8 @@ public OptionsWithDictionary() public Dictionary StringDictionary { get; set; } + public IDictionary IDictionaryNoSetter { get; } = new Dictionary(); + public Dictionary ObjectDictionary { get; set; } public Dictionary> ISetDictionary { get; set; } From 5634242a3a8bd4d2f35e063537d7350939162e80 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 16 Sep 2022 15:51:30 -0700 Subject: [PATCH 09/15] [release/7.0-rc2] Ensure Max/Min for floating-point on x86/x64 are not handled as commutative (#75761) * Adding a regression test for sixlabors/imagesharp#2117 * Ensure Max/Min for floating-point on x86/x64 are not handled as commutative * Applying formatting patch Co-authored-by: Tanner Gooding --- src/coreclr/jit/gentree.cpp | 41 +++++++++++++++++-- src/coreclr/jit/hwintrinsic.h | 17 ++++++++ src/coreclr/jit/hwintrinsiclistxarch.h | 12 +++--- .../ImageSharp_2117/ImageSharp_2117.cs | 37 +++++++++++++++++ .../ImageSharp_2117/ImageSharp_2117.csproj | 13 ++++++ 5 files changed, 111 insertions(+), 9 deletions(-) create mode 100644 src/tests/JIT/Regression/JitBlue/ImageSharp_2117/ImageSharp_2117.cs create mode 100644 src/tests/JIT/Regression/JitBlue/ImageSharp_2117/ImageSharp_2117.csproj diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 3b7c12f25ccb12..1f9015cf8760e4 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -18631,10 +18631,45 @@ bool GenTree::isCommutativeHWIntrinsic() const assert(gtOper == GT_HWINTRINSIC); #ifdef TARGET_XARCH - return HWIntrinsicInfo::IsCommutative(AsHWIntrinsic()->GetHWIntrinsicId()); -#else - return false; + const GenTreeHWIntrinsic* node = AsHWIntrinsic(); + NamedIntrinsic id = node->GetHWIntrinsicId(); + + if (HWIntrinsicInfo::IsCommutative(id)) + { + return true; + } + + if (HWIntrinsicInfo::IsMaybeCommutative(id)) + { + switch (id) + { + case NI_SSE_Max: + case NI_SSE_Min: + { + return false; + } + + case NI_SSE2_Max: + case NI_SSE2_Min: + { + return !varTypeIsFloating(node->GetSimdBaseType()); + } + + case NI_AVX_Max: + case NI_AVX_Min: + { + return false; + } + + default: + { + unreached(); + } + } + } #endif // TARGET_XARCH + + return false; } bool GenTree::isContainableHWIntrinsic() const diff --git a/src/coreclr/jit/hwintrinsic.h b/src/coreclr/jit/hwintrinsic.h index 88e5b4ae575059..b1299df1c1f1cf 100644 --- a/src/coreclr/jit/hwintrinsic.h +++ b/src/coreclr/jit/hwintrinsic.h @@ -153,6 +153,11 @@ enum HWIntrinsicFlag : unsigned int // the intrinsic can be used on hardware with AVX but not AVX2 support HW_Flag_AvxOnlyCompatible = 0x40000, + // MaybeCommutative + // - if a binary-op intrinsic is maybe commutative (e.g., Max or Min for float/double), its op1 can possibly be + // contained + HW_Flag_MaybeCommutative = 0x80000, + #elif defined(TARGET_ARM64) // The intrinsic has an immediate operand // - the value can be (and should be) encoded in a corresponding instruction when the operand value is constant @@ -626,6 +631,18 @@ struct HWIntrinsicInfo return (flags & HW_Flag_Commutative) != 0; } + static bool IsMaybeCommutative(NamedIntrinsic id) + { + HWIntrinsicFlag flags = lookupFlags(id); +#if defined(TARGET_XARCH) + return (flags & HW_Flag_MaybeCommutative) != 0; +#elif defined(TARGET_ARM64) + return false; +#else +#error Unsupported platform +#endif + } + static bool RequiresCodegen(NamedIntrinsic id) { HWIntrinsicFlag flags = lookupFlags(id); diff --git a/src/coreclr/jit/hwintrinsiclistxarch.h b/src/coreclr/jit/hwintrinsiclistxarch.h index 7068e39bd1b560..0fff3a98d3bec3 100644 --- a/src/coreclr/jit/hwintrinsiclistxarch.h +++ b/src/coreclr/jit/hwintrinsiclistxarch.h @@ -290,9 +290,9 @@ HARDWARE_INTRINSIC(SSE, LoadHigh, HARDWARE_INTRINSIC(SSE, LoadLow, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_movlps, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_NoRMWSemantics) HARDWARE_INTRINSIC(SSE, LoadScalarVector128, 16, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_movss, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_NoRMWSemantics) HARDWARE_INTRINSIC(SSE, LoadVector128, 16, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_movups, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_NoRMWSemantics) -HARDWARE_INTRINSIC(SSE, Max, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_maxps, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_Commutative) +HARDWARE_INTRINSIC(SSE, Max, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_maxps, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_MaybeCommutative) HARDWARE_INTRINSIC(SSE, MaxScalar, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_maxss, INS_invalid}, HW_Category_SIMDScalar, HW_Flag_CopyUpperBits) -HARDWARE_INTRINSIC(SSE, Min, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_minps, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_Commutative) +HARDWARE_INTRINSIC(SSE, Min, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_minps, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_MaybeCommutative) HARDWARE_INTRINSIC(SSE, MinScalar, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_minss, INS_invalid}, HW_Category_SIMDScalar, HW_Flag_CopyUpperBits) HARDWARE_INTRINSIC(SSE, MoveHighToLow, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_movhlps, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_NoContainment) HARDWARE_INTRINSIC(SSE, MoveLowToHigh, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_movlhps, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_NoContainment) @@ -403,10 +403,10 @@ HARDWARE_INTRINSIC(SSE2, LoadLow, HARDWARE_INTRINSIC(SSE2, LoadScalarVector128, 16, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_movd, INS_movd, INS_movq, INS_movq, INS_invalid, INS_movsdsse2}, HW_Category_MemoryLoad, HW_Flag_NoRMWSemantics) HARDWARE_INTRINSIC(SSE2, LoadVector128, 16, 1, {INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_invalid, INS_movupd}, HW_Category_MemoryLoad, HW_Flag_NoRMWSemantics) HARDWARE_INTRINSIC(SSE2, MaskMove, 16, 3, {INS_maskmovdqu, INS_maskmovdqu, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryStore, HW_Flag_NoContainment|HW_Flag_NoRMWSemantics|HW_Flag_BaseTypeFromSecondArg) -HARDWARE_INTRINSIC(SSE2, Max, 16, 2, {INS_invalid, INS_pmaxub, INS_pmaxsw, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_maxpd}, HW_Category_SimpleSIMD, HW_Flag_Commutative) +HARDWARE_INTRINSIC(SSE2, Max, 16, 2, {INS_invalid, INS_pmaxub, INS_pmaxsw, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_maxpd}, HW_Category_SimpleSIMD, HW_Flag_MaybeCommutative) HARDWARE_INTRINSIC(SSE2, MemoryFence, 0, 0, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Special, HW_Flag_NoContainment|HW_Flag_NoRMWSemantics) HARDWARE_INTRINSIC(SSE2, MaxScalar, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_maxsd}, HW_Category_SIMDScalar, HW_Flag_CopyUpperBits) -HARDWARE_INTRINSIC(SSE2, Min, 16, 2, {INS_invalid, INS_pminub, INS_pminsw, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_minpd}, HW_Category_SimpleSIMD, HW_Flag_Commutative) +HARDWARE_INTRINSIC(SSE2, Min, 16, 2, {INS_invalid, INS_pminub, INS_pminsw, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_minpd}, HW_Category_SimpleSIMD, HW_Flag_MaybeCommutative) HARDWARE_INTRINSIC(SSE2, MinScalar, 16, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_minsd}, HW_Category_SIMDScalar, HW_Flag_CopyUpperBits) HARDWARE_INTRINSIC(SSE2, MoveMask, 16, 1, {INS_pmovmskb, INS_pmovmskb, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_movmskpd}, HW_Category_SimpleSIMD, HW_Flag_NoContainment|HW_Flag_NoRMWSemantics|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(SSE2, MoveScalar, 16, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_movq, INS_movq, INS_invalid, INS_movsdsse2}, HW_Category_SIMDScalar, HW_Flag_NoContainment) @@ -598,8 +598,8 @@ HARDWARE_INTRINSIC(AVX, InsertVector128, HARDWARE_INTRINSIC(AVX, LoadAlignedVector256, 32, 1, {INS_movdqa, INS_movdqa, INS_movdqa, INS_movdqa, INS_movdqa, INS_movdqa, INS_movdqa, INS_movdqa, INS_movaps, INS_movapd}, HW_Category_MemoryLoad, HW_Flag_NoRMWSemantics) HARDWARE_INTRINSIC(AVX, LoadDquVector256, 32, 1, {INS_lddqu, INS_lddqu, INS_lddqu, INS_lddqu, INS_lddqu, INS_lddqu, INS_lddqu, INS_lddqu, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_NoRMWSemantics) HARDWARE_INTRINSIC(AVX, LoadVector256, 32, 1, {INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movups, INS_movupd}, HW_Category_MemoryLoad, HW_Flag_NoRMWSemantics) -HARDWARE_INTRINSIC(AVX, Max, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_maxps, INS_maxpd}, HW_Category_SimpleSIMD, HW_Flag_Commutative) -HARDWARE_INTRINSIC(AVX, Min, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_minps, INS_minpd}, HW_Category_SimpleSIMD, HW_Flag_Commutative) +HARDWARE_INTRINSIC(AVX, Max, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_maxps, INS_maxpd}, HW_Category_SimpleSIMD, HW_Flag_MaybeCommutative) +HARDWARE_INTRINSIC(AVX, Min, 32, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_minps, INS_minpd}, HW_Category_SimpleSIMD, HW_Flag_MaybeCommutative) HARDWARE_INTRINSIC(AVX, MaskLoad, -1, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vmaskmovps, INS_vmaskmovpd}, HW_Category_MemoryLoad, HW_Flag_NoFlag) HARDWARE_INTRINSIC(AVX, MaskStore, -1, 3, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vmaskmovps, INS_vmaskmovpd}, HW_Category_MemoryStore, HW_Flag_NoContainment|HW_Flag_BaseTypeFromSecondArg) HARDWARE_INTRINSIC(AVX, MoveMask, 32, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_movmskps, INS_movmskpd}, HW_Category_SimpleSIMD, HW_Flag_NoContainment|HW_Flag_BaseTypeFromFirstArg) diff --git a/src/tests/JIT/Regression/JitBlue/ImageSharp_2117/ImageSharp_2117.cs b/src/tests/JIT/Regression/JitBlue/ImageSharp_2117/ImageSharp_2117.cs new file mode 100644 index 00000000000000..415d9b00f5c8a6 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/ImageSharp_2117/ImageSharp_2117.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +public unsafe class ImageSharp_2117 +{ + public static int Main(string[] args) + { + if (Sse.IsSupported) + { + Vector128 fnan = Vector128.Create(float.NaN); + Vector128 res1 = Sse.Max(Sse.LoadVector128((float*)(&fnan)), Vector128.Zero); + Vector128 res2 = Sse.Min(Sse.LoadVector128((float*)(&fnan)), Vector128.Zero); + + if (float.IsNaN(res1[0]) || float.IsNaN(res2[0])) + { + return 0; + } + } + + if (Sse2.IsSupported) + { + Vector128 dnan = Vector128.Create(double.NaN); + Vector128 res3 = Sse2.Max(Sse2.LoadVector128((double*)(&dnan)), Vector128.Zero); + Vector128 res4 = Sse2.Min(Sse2.LoadVector128((double*)(&dnan)), Vector128.Zero); + + if (double.IsNaN(res3[0]) || double.IsNaN(res4[0])) + { + return 0; + } + } + + return 100; + } +} diff --git a/src/tests/JIT/Regression/JitBlue/ImageSharp_2117/ImageSharp_2117.csproj b/src/tests/JIT/Regression/JitBlue/ImageSharp_2117/ImageSharp_2117.csproj new file mode 100644 index 00000000000000..e7284d26ab2f18 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/ImageSharp_2117/ImageSharp_2117.csproj @@ -0,0 +1,13 @@ + + + Exe + True + + + None + True + + + + + From b23dc07bd7363fcd986a17230c2734bc656e71a7 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 19 Sep 2022 11:54:12 -0700 Subject: [PATCH 10/15] [release/7.0-rc2] Update dependencies from dotnet/emsdk (#75707) * Update dependencies from https://github.com/dotnet/emsdk build 20220914.1 Microsoft.NET.Workload.Emscripten.net6.Manifest-7.0.100 , Microsoft.NET.Workload.Emscripten.net7.Manifest-7.0.100 From Version 7.0.0-rc.2.22459.3 -> To Version 7.0.0-rc.2.22464.1 * Update dependencies from https://github.com/dotnet/emsdk build 20220915.1 Microsoft.NET.Workload.Emscripten.net6.Manifest-7.0.100 , Microsoft.NET.Workload.Emscripten.net7.Manifest-7.0.100 From Version 7.0.0-rc.2.22459.3 -> To Version 7.0.0-rc.2.22465.1 Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 5b5d22984f53f8..479196edabbaca 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -48,13 +48,13 @@ https://github.com/dotnet/command-line-api 5618b2d243ccdeb5c7e50a298b33b13036b4351b - + https://github.com/dotnet/emsdk - 76e6b338e87093ddc9b3b650f027706aba65d388 + 6625add9a3eadc2954af0311be35290cfefcddb0 - + https://github.com/dotnet/emsdk - 76e6b338e87093ddc9b3b650f027706aba65d388 + 6625add9a3eadc2954af0311be35290cfefcddb0 diff --git a/eng/Versions.props b/eng/Versions.props index 7d7b50789a5cc0..0b0a0cc273647e 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -22,8 +22,8 @@ false $(AssemblyVersion) true - 7.0.0-rc.2.22459.3 - 7.0.0-rc.2.22459.3 + 7.0.0-rc.2.22465.1 + 7.0.0-rc.2.22465.1 1.1.2-beta1.22403.2 - 7.0.0-preview-20220915.1 + 7.0.0-preview-20220916.1 7.0.100-1.22423.4 $(MicrosoftNETILLinkTasksVersion) From 39fce88425aa362b22c483191775c4a7b404e99b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 19 Sep 2022 13:34:54 -0700 Subject: [PATCH 13/15] fix behavior of JMC (#75833) Co-authored-by: Thays Grazia --- .../BrowserDebugProxy/MemberObjectsExplorer.cs | 17 ++++++++++------- .../debugger/BrowserDebugProxy/MonoSDBHelper.cs | 3 ++- .../BrowserDebugProxy/ValueTypeClass.cs | 2 +- .../debugger/DebuggerTestSuite/MiscTests.cs | 6 +++++- .../test.cs | 4 ++-- .../test.cs | 4 ++-- .../tests/debugger-test/debugger-test.cs | 4 ++-- 7 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MemberObjectsExplorer.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MemberObjectsExplorer.cs index 24f4d8522cb845..38c6540b812d6c 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MemberObjectsExplorer.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MemberObjectsExplorer.cs @@ -238,6 +238,9 @@ public static async Task ExpandFieldValues( 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(), out DebuggerBrowsableState fieldState) || fieldState == DebuggerBrowsableState.Collapsed) { @@ -311,7 +314,7 @@ public static async Task> ExpandPropertyValues( int typeId, string typeName, ArraySegment getterParamsBuffer, - bool isAutoExpandable, + GetObjectCommandOptions getCommandOptions, DotnetObjectId objectId, bool isValueType, bool isOwn, @@ -347,6 +350,10 @@ public static async Task> ExpandPropertyValues( 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); @@ -454,7 +461,7 @@ async Task AddProperty( { 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 { @@ -568,10 +575,6 @@ public static async Task GetObjectMemberValues( 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 +607,7 @@ public static async Task GetObjectMemberValues( typeId, typeName, getPropertiesParamBuffer, - getCommandType.HasFlag(GetObjectCommandOptions.ForDebuggerProxyAttribute), + getCommandType, id, isValueType: false, isOwn, diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs index 8ff9f8ae9af99e..fb17d2e72453b3 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -60,7 +60,8 @@ internal enum GetObjectCommandOptions ForDebuggerProxyAttribute = 8, ForDebuggerDisplayAttribute = 16, WithProperties = 32, - JustMyCode = 64 + JustMyCode = 64, + AutoExpandable = 128 } internal enum CommandSet { diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/ValueTypeClass.cs b/src/mono/wasm/debugger/BrowserDebugProxy/ValueTypeClass.cs index c2a2513f00ba19..07b2036caacb3b 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/ValueTypeClass.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/ValueTypeClass.cs @@ -293,7 +293,7 @@ public async Task ExpandPropertyValues(MonoSDBHelper sdbHelper, bool splitMember typeId, className, Buffer, - autoExpand, + autoExpand ? GetObjectCommandOptions.AutoExpandable : GetObjectCommandOptions.None, Id, isValueType: true, isOwn: i == 0, diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs index 9973811a56e499..e05f4d682db641 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/MiscTests.cs @@ -1067,7 +1067,11 @@ await EvaluateAndCheck( { 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 { 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 af11a6329d0d26..1626d47d5d4e1f 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 @@ public class ClassNonUserCodeToInheritThatInheritsFromNormalClass : NormalClass 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 5dcdc29f93f2d0..4899b8869ef402 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 @@ public class ClassWithoutDebugSymbolsToInherit 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-test.cs b/src/mono/wasm/debugger/tests/debugger-test/debugger-test.cs index b8f52c788f0d2b..73a225cf5e16c4 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() { From a66c066c39ba42185d221978a2f01eec3dccb849 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 19 Sep 2022 13:53:03 -0700 Subject: [PATCH 14/15] hide interop delegates from intelisense (#75825) Co-authored-by: pavelsavara --- .../ref/System.Runtime.InteropServices.JavaScript.cs | 2 ++ .../JavaScript/Marshaling/JSMarshalerArgument.Task.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/ref/System.Runtime.InteropServices.JavaScript.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/ref/System.Runtime.InteropServices.JavaScript.cs index 54b2c5352f8c21..c910e7f8228c5d 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/ref/System.Runtime.InteropServices.JavaScript.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/ref/System.Runtime.InteropServices.JavaScript.cs @@ -201,7 +201,9 @@ public sealed class JSMarshalerType [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public struct JSMarshalerArgument { + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public delegate void ArgumentToManagedCallback(ref JSMarshalerArgument arg, out T value); + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public delegate void ArgumentToJSCallback(ref JSMarshalerArgument arg, T value); public void Initialize() { throw null; } public void ToManaged(out bool value) { throw null; } diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Task.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Task.cs index f3101e881fe9ac..13876867c8a544 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Task.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Task.cs @@ -13,12 +13,14 @@ public partial struct JSMarshalerArgument /// Helps with marshaling of the Task result or Function arguments. /// It's used by JSImport code generator and should not be used by developers in source code. /// + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public delegate void ArgumentToManagedCallback(ref JSMarshalerArgument arg, out T value); /// /// Helps with marshaling of the Task result or Function arguments. /// It's used by JSImport code generator and should not be used by developers in source code. /// + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public delegate void ArgumentToJSCallback(ref JSMarshalerArgument arg, T value); /// From 30561351db1df41b58cde04453237d43871380d1 Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Mon, 19 Sep 2022 23:18:49 +0200 Subject: [PATCH 15/15] * fix missing managed stack trace on managed exceptions marshaled to JS (#75799) * override `get stack` * fix Firefox tests --- .../JavaScript/Interop/JavaScriptExports.cs | 28 ++++++++- .../JavaScript/JSImportExportTest.cs | 23 +++++++- .../JavaScript/JavaScriptTestHelper.cs | 58 ++++++++++--------- .../JavaScript/JavaScriptTestHelper.mjs | 26 ++++++++- src/mono/wasm/runtime/logging.ts | 5 +- src/mono/wasm/runtime/managed-exports.ts | 21 ++++++- src/mono/wasm/runtime/marshal-to-js.ts | 8 +-- src/mono/wasm/runtime/marshal.ts | 30 +++++++--- src/mono/wasm/runtime/types.ts | 3 + 9 files changed, 155 insertions(+), 47 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs index 5b932edb7965dc..2fbf3f448f5cd5 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs @@ -124,7 +124,7 @@ public static void ReleaseJSOwnedObjectByGCHandle(JSMarshalerArgument* arguments public static void CreateTaskCallback(JSMarshalerArgument* arguments_buffer) { ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; // initialized by caller in alloc_stack_frame() - ref JSMarshalerArgument arg_return = ref arguments_buffer[1]; // used as return vaule + ref JSMarshalerArgument arg_return = ref arguments_buffer[1]; // used as return value try { JSHostImplementation.TaskCallback holder = new JSHostImplementation.TaskCallback(); @@ -195,6 +195,32 @@ public static void CompleteTask(JSMarshalerArgument* arguments_buffer) } } + [MethodImpl(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425 + // the marshaled signature is: + // string GetManagedStackTrace(GCHandle exception) + public static void GetManagedStackTrace(JSMarshalerArgument* arguments_buffer) + { + ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; // initialized by caller in alloc_stack_frame() + ref JSMarshalerArgument arg_return = ref arguments_buffer[1]; // used as return value + ref JSMarshalerArgument arg_1 = ref arguments_buffer[2];// initialized and set by caller + try + { + GCHandle exception_gc_handle = (GCHandle)arg_1.slot.GCHandle; + if (exception_gc_handle.Target is Exception exception) + { + arg_return.ToJS(exception.StackTrace); + } + else + { + throw new InvalidOperationException("Exception is null"); + } + } + catch (Exception ex) + { + arg_exc.ToJS(ex); + } + } + #if FEATURE_WASM_THREADS [MethodImpl(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425 diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSImportExportTest.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSImportExportTest.cs index a8966dcaf521d0..79e9e3e0cecf50 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSImportExportTest.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSImportExportTest.cs @@ -1358,11 +1358,28 @@ public void JsExportException(Exception value, string clazz) [Fact] public void JsExportThrows() { - var ex = Assert.Throws(() => JavaScriptTestHelper.invoke1_String("-t-e-s-t-", nameof(JavaScriptTestHelper.Throw))); + var ex = Assert.Throws(() => JavaScriptTestHelper.invoke1_String("-t-e-s-t-", nameof(JavaScriptTestHelper.ThrowFromJSExport))); Assert.DoesNotContain("Unexpected error", ex.Message); Assert.Contains("-t-e-s-t-", ex.Message); } + [Fact] + public void JsExportCatchToString() + { + var toString = JavaScriptTestHelper.catch1toString("-t-e-s-t-", nameof(JavaScriptTestHelper.ThrowFromJSExport)); + Assert.DoesNotContain("Unexpected error", toString); + Assert.Contains("-t-e-s-t-", toString); + Assert.DoesNotContain(nameof(JavaScriptTestHelper.ThrowFromJSExport), toString); + } + + [Fact] + public void JsExportCatchStack() + { + var stack = JavaScriptTestHelper.catch1stack("-t-e-s-t-", nameof(JavaScriptTestHelper.ThrowFromJSExport)); + Assert.Contains(nameof(JavaScriptTestHelper.ThrowFromJSExport), stack); + Assert.Contains("catch1stack", stack); + } + #endregion Exception #region JSObject @@ -1906,12 +1923,12 @@ private void JsImportTest(T value var exThrow0 = Assert.Throws(() => JavaScriptTestHelper.throw0()); Assert.Contains("throw-0-msg", exThrow0.Message); Assert.DoesNotContain(" at ", exThrow0.Message); - Assert.Contains(" at Module.throw0", exThrow0.StackTrace); + Assert.Contains("throw0fn", exThrow0.StackTrace); var exThrow1 = Assert.Throws(() => throw1(value)); Assert.Contains("throw1-msg", exThrow1.Message); Assert.DoesNotContain(" at ", exThrow1.Message); - Assert.Contains(" at Module.throw1", exThrow1.StackTrace); + Assert.Contains("throw1fn", exThrow1.StackTrace); // anything is a system.object, sometimes it would be JSObject wrapper if (typeof(T).IsPrimitive) diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs index b5a89b7f48fe98..67fe77cfe5fe80 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs @@ -32,8 +32,14 @@ public static void ConsoleWriteLine([JSMarshalAs] string message) Console.WriteLine(message); } + [JSImport("catch1toString", "JavaScriptTestHelper")] + public static partial string catch1toString(string message, string functionName); + + [JSImport("catch1stack", "JavaScriptTestHelper")] + public static partial string catch1stack(string message, string functionName); + [JSExport] - public static void Throw(string message) + public static void ThrowFromJSExport(string message) { throw new ArgumentException(message); } @@ -57,7 +63,7 @@ public static DateTime Now() [return: JSMarshalAs] internal static partial string getClass1(); - [JSImport("throw0", "JavaScriptTestHelper")] + [JSImport("throw0fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial void throw0(); @@ -215,7 +221,7 @@ internal static partial void Relaxed(string a1, Exception ex, [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_Int32([JSMarshalAs] int value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial int throw1_Int32([JSMarshalAs] int value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -241,7 +247,7 @@ public static int EchoInt32([JSMarshalAs] int arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_String([JSMarshalAs] string value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial string throw1_String([JSMarshalAs] string value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -273,7 +279,7 @@ public static string EchoString([JSMarshalAs] string arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_Object([JSMarshalAs] object value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial object throw1_Object([JSMarshalAs] object value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -299,7 +305,7 @@ public static object EchoObject([JSMarshalAs] object arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_Exception([JSMarshalAs] Exception value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial Exception throw1_Exception([JSMarshalAs] Exception value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -431,7 +437,7 @@ public static Func BackFuncOfIntInt([JSMarshalAs] internal static partial bool identity1_Boolean([JSMarshalAs] bool value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool throw1_Boolean([JSMarshalAs] bool value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -458,7 +464,7 @@ public static bool EchoBoolean([JSMarshalAs] bool arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_Char([JSMarshalAs] char value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial char throw1_Char([JSMarshalAs] char value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -484,7 +490,7 @@ public static char EchoChar([JSMarshalAs] char arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_Byte([JSMarshalAs] byte value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial byte throw1_Byte([JSMarshalAs] byte value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -510,7 +516,7 @@ public static byte EchoByte([JSMarshalAs] byte arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_Int16([JSMarshalAs] short value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial short throw1_Int16([JSMarshalAs] short value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -536,7 +542,7 @@ public static short EchoInt16([JSMarshalAs] short arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_Int52([JSMarshalAs] long value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial long throw1_Int52([JSMarshalAs] long value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -562,7 +568,7 @@ public static long EchoInt52([JSMarshalAs] long arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_BigInt64([JSMarshalAs] long value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial long throw1_BigInt64([JSMarshalAs] long value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -588,7 +594,7 @@ public static long EchoBigInt64([JSMarshalAs] long arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_Double([JSMarshalAs] double value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial double throw1_Double([JSMarshalAs] double value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -614,7 +620,7 @@ public static double EchoDouble([JSMarshalAs] double arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_Single([JSMarshalAs] float value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial float throw1_Single([JSMarshalAs] float value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -640,7 +646,7 @@ public static float EchoSingle([JSMarshalAs] float arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_IntPtr([JSMarshalAs] IntPtr value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial IntPtr throw1_IntPtr([JSMarshalAs] IntPtr value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -667,7 +673,7 @@ public static IntPtr EchoIntPtr([JSMarshalAs] IntPtr arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal unsafe static partial bool identity1_VoidPtr([JSMarshalAs] void* value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal unsafe static partial void* throw1_VoidPtr([JSMarshalAs] void* value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -693,7 +699,7 @@ public static IntPtr EchoIntPtr([JSMarshalAs] IntPtr arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_DateTime([JSMarshalAs] DateTime value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial DateTime throw1_DateTime([JSMarshalAs] DateTime value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -719,7 +725,7 @@ public static DateTime EchoDateTime([JSMarshalAs] DateTime arg1) [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_DateTimeOffset([JSMarshalAs] DateTimeOffset value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial DateTimeOffset throw1_DateTimeOffset([JSMarshalAs] DateTimeOffset value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -746,7 +752,7 @@ public static DateTimeOffset EchoDateTimeOffset([JSMarshalAs] DateT [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_NullableBoolean([JSMarshalAs] bool? value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool? throw1_NullableBoolean([JSMarshalAs] bool? value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -773,7 +779,7 @@ public static DateTimeOffset EchoDateTimeOffset([JSMarshalAs] DateT [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_NullableInt32([JSMarshalAs] int? value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial int? throw1_NullableInt32([JSMarshalAs] int? value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -800,7 +806,7 @@ public static DateTimeOffset EchoDateTimeOffset([JSMarshalAs] DateT [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_NullableBigInt64([JSMarshalAs] long? value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial long? throw1_NullableBigInt64([JSMarshalAs] long? value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -827,7 +833,7 @@ public static DateTimeOffset EchoDateTimeOffset([JSMarshalAs] DateT [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_NullableIntPtr([JSMarshalAs] IntPtr? value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial IntPtr? throw1_NullableIntPtr([JSMarshalAs] IntPtr? value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -854,7 +860,7 @@ public static DateTimeOffset EchoDateTimeOffset([JSMarshalAs] DateT [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_NullableDouble([JSMarshalAs] double? value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial double? throw1_NullableDouble([JSMarshalAs] double? value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -881,7 +887,7 @@ public static DateTimeOffset EchoDateTimeOffset([JSMarshalAs] DateT [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_NullableDateTime([JSMarshalAs] DateTime? value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial DateTime? throw1_NullableDateTime([JSMarshalAs] DateTime? value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -907,7 +913,7 @@ public static DateTimeOffset EchoDateTimeOffset([JSMarshalAs] DateT [JSImport("identity1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial bool identity1_JSObject([JSMarshalAs] JSObject value); - [JSImport("throw1", "JavaScriptTestHelper")] + [JSImport("throw1fn", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial JSObject throw1_JSObject([JSMarshalAs] JSObject value); [JSImport("invoke1", "JavaScriptTestHelper")] @@ -1236,4 +1242,4 @@ public partial record struct NestedRecordStruct [System.Runtime.InteropServices.JavaScript.JSExport] public static string EchoString(string message) => message + "85"; } -} \ No newline at end of file +} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.mjs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.mjs index 314e715e16b815..0811be477c64f6 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.mjs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.mjs @@ -105,12 +105,34 @@ export function retrieve1() { return val; } -export function throw0() { +export function throw0fn() { //console.log(`throw0()`) throw new Error('throw-0-msg'); } -export function throw1(arg1) { +export function catch1toString(message, functionName) { + const JavaScriptTestHelper = dllExports.System.Runtime.InteropServices.JavaScript.Tests.JavaScriptTestHelper; + const fn = JavaScriptTestHelper[functionName]; + try { + fn(message); + return "bad"; + } catch (err) { + return err.toString(); + } +} + +export function catch1stack(message, functionName) { + const JavaScriptTestHelper = dllExports.System.Runtime.InteropServices.JavaScript.Tests.JavaScriptTestHelper; + const fn = JavaScriptTestHelper[functionName]; + try { + fn(message); + return "bad"; + } catch (err) { + return err.stack; + } +} + +export function throw1fn(arg1) { //console.log(`throw1(arg1:${arg1 !== null ? arg1 : ''})`) throw new Error('throw1-msg ' + arg1); } diff --git a/src/mono/wasm/runtime/logging.ts b/src/mono/wasm/runtime/logging.ts index 2d40d7c4d20e29..cdb2d00dba723d 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 09f6d9a33d0241..65186e4c0b270f 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 318d10580ffec0..aac0155406c4ec 100644 --- a/src/mono/wasm/runtime/marshal-to-js.ts +++ b/src/mono/wasm/runtime/marshal-to-js.ts @@ -32,7 +32,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); @@ -353,7 +353,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; @@ -383,7 +383,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); @@ -462,7 +462,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(buffer_ptr, index); - result[index] = _marshal_string_to_js(element_arg); + result[index] = marshal_string_to_js(element_arg); } cwraps.mono_wasm_deregister_root(buffer_ptr); } diff --git a/src/mono/wasm/runtime/marshal.ts b/src/mono/wasm/runtime/marshal.ts index 77bec6b6aa762f..f20a620c5fb76b 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"; @@ -316,13 +316,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 = (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 { @@ -332,10 +350,6 @@ export class ManagedError extends Error implements IDisposable { get isDisposed(): boolean { return (this)[js_owned_gc_handle_symbol] === GCHandleNull; } - - toString(): string { - return `ManagedError(gc_handle: ${(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 ea01f16a9e5c85..3664532e54c5e9 100644 --- a/src/mono/wasm/runtime/types.ts +++ b/src/mono/wasm/runtime/types.ts @@ -411,6 +411,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) => any;