From 67d8210f39d1910850672725dc62c97a01f57025 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Tue, 10 Mar 2026 22:27:55 +0000 Subject: [PATCH 1/5] Backflow from https://github.com/dotnet/dotnet / 3499749 build 305514 [[ commit created by automation ]] --- eng/Signing.props | 1 + eng/Subsets.props | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/eng/Signing.props b/eng/Signing.props index 3d671fb0506f1a..6a7c3c05e55256 100644 --- a/eng/Signing.props +++ b/eng/Signing.props @@ -44,6 +44,7 @@ + diff --git a/eng/Subsets.props b/eng/Subsets.props index 61b5f0c818d518..21db6bbdcb8609 100644 --- a/eng/Subsets.props +++ b/eng/Subsets.props @@ -485,9 +485,7 @@ - - - + From 79edb6a002698427e01cecd94800da750d857846 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Tue, 10 Mar 2026 22:28:01 +0000 Subject: [PATCH 2/5] Update dependencies from build 305514 Updated Dependencies: Microsoft.CodeAnalysis, Microsoft.CodeAnalysis.Analyzers, Microsoft.CodeAnalysis.CSharp, Microsoft.Net.Compilers.Toolset (Version 5.6.0-2.26152.106 -> 5.6.0-2.26160.107) Microsoft.CodeAnalysis.NetAnalyzers, Microsoft.DotNet.ApiCompat.Task, Microsoft.NET.Workload.Emscripten.Current.Manifest-11.0.100.Transport (Version 11.0.100-preview.3.26152.106 -> 11.0.100-preview.3.26160.107) Microsoft.DotNet.Arcade.Sdk, Microsoft.DotNet.Build.Tasks.Archives, Microsoft.DotNet.Build.Tasks.Feed, Microsoft.DotNet.Build.Tasks.Installers, Microsoft.DotNet.Build.Tasks.Packaging, Microsoft.DotNet.Build.Tasks.TargetFramework, Microsoft.DotNet.Build.Tasks.Templating, Microsoft.DotNet.Build.Tasks.Workloads, Microsoft.DotNet.CodeAnalysis, Microsoft.DotNet.GenAPI, Microsoft.DotNet.GenFacades, Microsoft.DotNet.Helix.Sdk, Microsoft.DotNet.PackageTesting, Microsoft.DotNet.RemoteExecutor, Microsoft.DotNet.SharedFramework.Sdk, Microsoft.DotNet.XliffTasks, Microsoft.DotNet.XUnitExtensions (Version 11.0.0-beta.26152.106 -> 11.0.0-beta.26160.107) Microsoft.DotNet.Cecil (Version 0.11.5-preview.26152.106 -> 0.11.5-preview.26160.107) Microsoft.DotNet.XUnitAssert, Microsoft.DotNet.XUnitConsoleRunner (Version 2.9.3-beta.26152.106 -> 2.9.3-beta.26160.107) Microsoft.NET.Sdk.IL, Microsoft.NETCore.App.Ref, Microsoft.NETCore.ILAsm, runtime.native.System.IO.Ports, System.Reflection.Metadata, System.Reflection.MetadataLoadContext, System.Text.Json (Version 11.0.0-preview.3.26152.106 -> 11.0.0-preview.3.26160.107) NuGet.Frameworks, NuGet.Packaging, NuGet.ProjectModel, NuGet.Versioning (Version 7.5.0-rc.15306 -> 7.5.0-rc.16107) System.CommandLine (Version 3.0.0-preview.3.26152.106 -> 3.0.0-preview.3.26160.107) runtime.linux-arm64.Microsoft.NETCore.Runtime.JIT.Tools, runtime.linux-x64.Microsoft.NETCore.Runtime.JIT.Tools, runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.JIT.Tools, runtime.linux-musl-x64.Microsoft.NETCore.Runtime.JIT.Tools, runtime.win-arm64.Microsoft.NETCore.Runtime.JIT.Tools, runtime.win-x64.Microsoft.NETCore.Runtime.JIT.Tools, runtime.osx-arm64.Microsoft.NETCore.Runtime.JIT.Tools, runtime.osx-x64.Microsoft.NETCore.Runtime.JIT.Tools, runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Libclang, runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk, runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools, runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Libclang, runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk, runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools, runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Libclang, runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk, runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools, runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Libclang, runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk, runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools, runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Libclang, runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk, runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools, runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Libclang, runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk, runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools, runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Libclang, runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk, runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools (Version 19.1.0-alpha.1.26116.1 -> 19.1.0-alpha.1.26152.1) runtime.linux-arm64.Microsoft.NETCore.Runtime.Wasm.Node.Transport, runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Wasm.Node.Transport, runtime.linux-x64.Microsoft.NETCore.Runtime.Wasm.Node.Transport, runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Wasm.Node.Transport, runtime.osx-arm64.Microsoft.NETCore.Runtime.Wasm.Node.Transport, runtime.osx-x64.Microsoft.NETCore.Runtime.Wasm.Node.Transport, runtime.win-arm64.Microsoft.NETCore.Runtime.Wasm.Node.Transport, runtime.win-x64.Microsoft.NETCore.Runtime.Wasm.Node.Transport (Version 11.0.0-alpha.1.26116.1 -> 11.0.0-alpha.1.26152.1) [[ commit created by automation ]] --- eng/Version.Details.props | 152 ++++----- eng/Version.Details.xml | 306 +++++++++--------- eng/common/core-templates/job/renovate.yml | 135 ++++++++ .../job/source-index-stage1.yml | 4 +- .../core-templates/post-build/post-build.yml | 8 +- eng/common/core-templates/stages/renovate.yml | 86 +++++ .../core-templates/steps/publish-logs.yml | 1 - eng/common/cross/build-rootfs.sh | 8 +- eng/common/renovate.env | 39 +++ global.json | 12 +- 10 files changed, 505 insertions(+), 246 deletions(-) create mode 100644 eng/common/core-templates/job/renovate.yml create mode 100644 eng/common/core-templates/stages/renovate.yml create mode 100644 eng/common/renovate.env diff --git a/eng/Version.Details.props b/eng/Version.Details.props index b4f5bc132e70e3..1f34138b21f086 100644 --- a/eng/Version.Details.props +++ b/eng/Version.Details.props @@ -6,88 +6,88 @@ This file should be imported by eng/Versions.props - 5.6.0-2.26152.106 - 5.6.0-2.26152.106 - 5.6.0-2.26152.106 - 11.0.100-preview.3.26152.106 - 11.0.100-preview.3.26152.106 - 11.0.0-beta.26152.106 - 11.0.0-beta.26152.106 - 11.0.0-beta.26152.106 - 11.0.0-beta.26152.106 - 11.0.0-beta.26152.106 - 11.0.0-beta.26152.106 - 11.0.0-beta.26152.106 - 11.0.0-beta.26152.106 - 0.11.5-preview.26152.106 - 11.0.0-beta.26152.106 - 11.0.0-beta.26152.106 - 11.0.0-beta.26152.106 - 11.0.0-beta.26152.106 - 11.0.0-beta.26152.106 - 11.0.0-beta.26152.106 - 11.0.0-beta.26152.106 - 11.0.0-beta.26152.106 - 2.9.3-beta.26152.106 - 2.9.3-beta.26152.106 - 11.0.0-beta.26152.106 - 5.6.0-2.26152.106 - 11.0.0-preview.3.26152.106 - 11.0.100-preview.3.26152.106 - 11.0.0-preview.3.26152.106 - 11.0.0-preview.3.26152.106 - 7.5.0-rc.15306 - 7.5.0-rc.15306 - 7.5.0-rc.15306 - 7.5.0-rc.15306 - 11.0.0-preview.3.26152.106 - 3.0.0-preview.3.26152.106 - 11.0.0-preview.3.26152.106 - 11.0.0-preview.3.26152.106 - 11.0.0-preview.3.26152.106 + 5.6.0-2.26160.107 + 5.6.0-2.26160.107 + 5.6.0-2.26160.107 + 11.0.100-preview.3.26160.107 + 11.0.100-preview.3.26160.107 + 11.0.0-beta.26160.107 + 11.0.0-beta.26160.107 + 11.0.0-beta.26160.107 + 11.0.0-beta.26160.107 + 11.0.0-beta.26160.107 + 11.0.0-beta.26160.107 + 11.0.0-beta.26160.107 + 11.0.0-beta.26160.107 + 0.11.5-preview.26160.107 + 11.0.0-beta.26160.107 + 11.0.0-beta.26160.107 + 11.0.0-beta.26160.107 + 11.0.0-beta.26160.107 + 11.0.0-beta.26160.107 + 11.0.0-beta.26160.107 + 11.0.0-beta.26160.107 + 11.0.0-beta.26160.107 + 2.9.3-beta.26160.107 + 2.9.3-beta.26160.107 + 11.0.0-beta.26160.107 + 5.6.0-2.26160.107 + 11.0.0-preview.3.26160.107 + 11.0.100-preview.3.26160.107 + 11.0.0-preview.3.26160.107 + 11.0.0-preview.3.26160.107 + 7.5.0-rc.16107 + 7.5.0-rc.16107 + 7.5.0-rc.16107 + 7.5.0-rc.16107 + 11.0.0-preview.3.26160.107 + 3.0.0-preview.3.26160.107 + 11.0.0-preview.3.26160.107 + 11.0.0-preview.3.26160.107 + 11.0.0-preview.3.26160.107 11.0.0-alpha.0.25625.1 11.0.0-alpha.1.26128.1 - 19.1.0-alpha.1.26116.1 - 19.1.0-alpha.1.26116.1 - 19.1.0-alpha.1.26116.1 - 19.1.0-alpha.1.26116.1 - 19.1.0-alpha.1.26116.1 - 19.1.0-alpha.1.26116.1 - 19.1.0-alpha.1.26116.1 - 19.1.0-alpha.1.26116.1 - 19.1.0-alpha.1.26116.1 - 19.1.0-alpha.1.26116.1 - 19.1.0-alpha.1.26116.1 - 19.1.0-alpha.1.26116.1 - 19.1.0-alpha.1.26116.1 - 19.1.0-alpha.1.26116.1 - 19.1.0-alpha.1.26116.1 - 19.1.0-alpha.1.26116.1 - 19.1.0-alpha.1.26116.1 - 19.1.0-alpha.1.26116.1 - 19.1.0-alpha.1.26116.1 - 19.1.0-alpha.1.26116.1 - 19.1.0-alpha.1.26116.1 - 19.1.0-alpha.1.26116.1 - 19.1.0-alpha.1.26116.1 - 19.1.0-alpha.1.26116.1 - 19.1.0-alpha.1.26116.1 - 19.1.0-alpha.1.26116.1 - 19.1.0-alpha.1.26116.1 - 19.1.0-alpha.1.26116.1 - 19.1.0-alpha.1.26116.1 + 19.1.0-alpha.1.26152.1 + 19.1.0-alpha.1.26152.1 + 19.1.0-alpha.1.26152.1 + 19.1.0-alpha.1.26152.1 + 19.1.0-alpha.1.26152.1 + 19.1.0-alpha.1.26152.1 + 19.1.0-alpha.1.26152.1 + 19.1.0-alpha.1.26152.1 + 19.1.0-alpha.1.26152.1 + 19.1.0-alpha.1.26152.1 + 19.1.0-alpha.1.26152.1 + 19.1.0-alpha.1.26152.1 + 19.1.0-alpha.1.26152.1 + 19.1.0-alpha.1.26152.1 + 19.1.0-alpha.1.26152.1 + 19.1.0-alpha.1.26152.1 + 19.1.0-alpha.1.26152.1 + 19.1.0-alpha.1.26152.1 + 19.1.0-alpha.1.26152.1 + 19.1.0-alpha.1.26152.1 + 19.1.0-alpha.1.26152.1 + 19.1.0-alpha.1.26152.1 + 19.1.0-alpha.1.26152.1 + 19.1.0-alpha.1.26152.1 + 19.1.0-alpha.1.26152.1 + 19.1.0-alpha.1.26152.1 + 19.1.0-alpha.1.26152.1 + 19.1.0-alpha.1.26152.1 + 19.1.0-alpha.1.26152.1 - 11.0.0-alpha.1.26116.1 - 11.0.0-alpha.1.26116.1 - 11.0.0-alpha.1.26116.1 - 11.0.0-alpha.1.26116.1 - 11.0.0-alpha.1.26116.1 - 11.0.0-alpha.1.26116.1 - 11.0.0-alpha.1.26116.1 - 11.0.0-alpha.1.26116.1 + 11.0.0-alpha.1.26152.1 + 11.0.0-alpha.1.26152.1 + 11.0.0-alpha.1.26152.1 + 11.0.0-alpha.1.26152.1 + 11.0.0-alpha.1.26152.1 + 11.0.0-alpha.1.26152.1 + 11.0.0-alpha.1.26152.1 + 11.0.0-alpha.1.26152.1 1.0.0-prerelease.26080.1 1.0.0-prerelease.26080.1 diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index d87a8ed73c30a3..ea0fb5fcf07f32 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,127 +1,127 @@ - + https://github.com/dotnet/icu 62bd07d81fecda4ae8a90919755e1acc9591e9e8 - + https://github.com/dotnet/llvm-project - 9b44df59bac85b9b40e7cf88b3c2908defc15003 + 07cf3afbd65f821dc9aec958aab04fa37fdce2ec - + https://github.com/dotnet/llvm-project - 9b44df59bac85b9b40e7cf88b3c2908defc15003 + 07cf3afbd65f821dc9aec958aab04fa37fdce2ec - + https://github.com/dotnet/llvm-project - 9b44df59bac85b9b40e7cf88b3c2908defc15003 + 07cf3afbd65f821dc9aec958aab04fa37fdce2ec - + https://github.com/dotnet/llvm-project - 9b44df59bac85b9b40e7cf88b3c2908defc15003 + 07cf3afbd65f821dc9aec958aab04fa37fdce2ec - + https://github.com/dotnet/llvm-project - 9b44df59bac85b9b40e7cf88b3c2908defc15003 + 07cf3afbd65f821dc9aec958aab04fa37fdce2ec - + https://github.com/dotnet/llvm-project - 9b44df59bac85b9b40e7cf88b3c2908defc15003 + 07cf3afbd65f821dc9aec958aab04fa37fdce2ec - + https://github.com/dotnet/llvm-project - 9b44df59bac85b9b40e7cf88b3c2908defc15003 + 07cf3afbd65f821dc9aec958aab04fa37fdce2ec - + https://github.com/dotnet/llvm-project - 9b44df59bac85b9b40e7cf88b3c2908defc15003 + 07cf3afbd65f821dc9aec958aab04fa37fdce2ec - + https://github.com/dotnet/dotnet - 5507d7a2f05bb6c073a055ead6ce1c4bbe396cda + 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 - + https://github.com/dotnet/dotnet - 5507d7a2f05bb6c073a055ead6ce1c4bbe396cda + 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 - + https://github.com/dotnet/dotnet - 5507d7a2f05bb6c073a055ead6ce1c4bbe396cda + 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 - + https://github.com/dotnet/dotnet - 5507d7a2f05bb6c073a055ead6ce1c4bbe396cda + 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 - + https://github.com/dotnet/dotnet - 5507d7a2f05bb6c073a055ead6ce1c4bbe396cda + 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 - + https://github.com/dotnet/dotnet - 5507d7a2f05bb6c073a055ead6ce1c4bbe396cda + 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 - + https://github.com/dotnet/dotnet - 5507d7a2f05bb6c073a055ead6ce1c4bbe396cda + 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 - + https://github.com/dotnet/dotnet - 5507d7a2f05bb6c073a055ead6ce1c4bbe396cda + 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 - + https://github.com/dotnet/dotnet - 5507d7a2f05bb6c073a055ead6ce1c4bbe396cda + 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 - + https://github.com/dotnet/dotnet - 5507d7a2f05bb6c073a055ead6ce1c4bbe396cda + 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 - + https://github.com/dotnet/dotnet - 5507d7a2f05bb6c073a055ead6ce1c4bbe396cda + 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 - + https://github.com/dotnet/dotnet - 5507d7a2f05bb6c073a055ead6ce1c4bbe396cda + 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 - + https://github.com/dotnet/dotnet - 5507d7a2f05bb6c073a055ead6ce1c4bbe396cda + 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 - + https://github.com/dotnet/dotnet - 5507d7a2f05bb6c073a055ead6ce1c4bbe396cda + 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 - + https://github.com/dotnet/dotnet - 5507d7a2f05bb6c073a055ead6ce1c4bbe396cda + 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 - + https://github.com/dotnet/dotnet - 5507d7a2f05bb6c073a055ead6ce1c4bbe396cda + 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 - + https://github.com/dotnet/dotnet - 5507d7a2f05bb6c073a055ead6ce1c4bbe396cda + 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 - + https://github.com/dotnet/dotnet - 5507d7a2f05bb6c073a055ead6ce1c4bbe396cda + 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 - + https://github.com/dotnet/dotnet - 5507d7a2f05bb6c073a055ead6ce1c4bbe396cda + 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 - + https://github.com/dotnet/dotnet - 5507d7a2f05bb6c073a055ead6ce1c4bbe396cda + 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 - + https://github.com/dotnet/dotnet - 5507d7a2f05bb6c073a055ead6ce1c4bbe396cda + 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 https://github.com/dotnet/runtime-assets @@ -175,117 +175,117 @@ https://github.com/dotnet/runtime-assets 027a725187a44e48327b7f8a0e181b9b9c036ae7 - + https://github.com/dotnet/llvm-project - 9b44df59bac85b9b40e7cf88b3c2908defc15003 + 07cf3afbd65f821dc9aec958aab04fa37fdce2ec - + https://github.com/dotnet/llvm-project - 9b44df59bac85b9b40e7cf88b3c2908defc15003 + 07cf3afbd65f821dc9aec958aab04fa37fdce2ec - + https://github.com/dotnet/llvm-project - 9b44df59bac85b9b40e7cf88b3c2908defc15003 + 07cf3afbd65f821dc9aec958aab04fa37fdce2ec - + https://github.com/dotnet/llvm-project - 9b44df59bac85b9b40e7cf88b3c2908defc15003 + 07cf3afbd65f821dc9aec958aab04fa37fdce2ec - + https://github.com/dotnet/llvm-project - 9b44df59bac85b9b40e7cf88b3c2908defc15003 + 07cf3afbd65f821dc9aec958aab04fa37fdce2ec - + https://github.com/dotnet/llvm-project - 9b44df59bac85b9b40e7cf88b3c2908defc15003 + 07cf3afbd65f821dc9aec958aab04fa37fdce2ec - + https://github.com/dotnet/llvm-project - 9b44df59bac85b9b40e7cf88b3c2908defc15003 + 07cf3afbd65f821dc9aec958aab04fa37fdce2ec - + https://github.com/dotnet/llvm-project - 9b44df59bac85b9b40e7cf88b3c2908defc15003 + 07cf3afbd65f821dc9aec958aab04fa37fdce2ec - + https://github.com/dotnet/llvm-project - 9b44df59bac85b9b40e7cf88b3c2908defc15003 + 07cf3afbd65f821dc9aec958aab04fa37fdce2ec - + https://github.com/dotnet/llvm-project - 9b44df59bac85b9b40e7cf88b3c2908defc15003 + 07cf3afbd65f821dc9aec958aab04fa37fdce2ec - + https://github.com/dotnet/llvm-project - 9b44df59bac85b9b40e7cf88b3c2908defc15003 + 07cf3afbd65f821dc9aec958aab04fa37fdce2ec - + https://github.com/dotnet/llvm-project - 9b44df59bac85b9b40e7cf88b3c2908defc15003 + 07cf3afbd65f821dc9aec958aab04fa37fdce2ec - + https://github.com/dotnet/llvm-project - 9b44df59bac85b9b40e7cf88b3c2908defc15003 + 07cf3afbd65f821dc9aec958aab04fa37fdce2ec - + https://github.com/dotnet/llvm-project - 9b44df59bac85b9b40e7cf88b3c2908defc15003 + 07cf3afbd65f821dc9aec958aab04fa37fdce2ec - + https://github.com/dotnet/llvm-project - 9b44df59bac85b9b40e7cf88b3c2908defc15003 + 07cf3afbd65f821dc9aec958aab04fa37fdce2ec - + https://github.com/dotnet/llvm-project - 9b44df59bac85b9b40e7cf88b3c2908defc15003 + 07cf3afbd65f821dc9aec958aab04fa37fdce2ec - + https://github.com/dotnet/llvm-project - 9b44df59bac85b9b40e7cf88b3c2908defc15003 + 07cf3afbd65f821dc9aec958aab04fa37fdce2ec - + https://github.com/dotnet/llvm-project - 9b44df59bac85b9b40e7cf88b3c2908defc15003 + 07cf3afbd65f821dc9aec958aab04fa37fdce2ec - + https://github.com/dotnet/llvm-project - 9b44df59bac85b9b40e7cf88b3c2908defc15003 + 07cf3afbd65f821dc9aec958aab04fa37fdce2ec - + https://github.com/dotnet/llvm-project - 9b44df59bac85b9b40e7cf88b3c2908defc15003 + 07cf3afbd65f821dc9aec958aab04fa37fdce2ec - + https://github.com/dotnet/llvm-project - 9b44df59bac85b9b40e7cf88b3c2908defc15003 + 07cf3afbd65f821dc9aec958aab04fa37fdce2ec - + https://github.com/dotnet/dotnet - 5507d7a2f05bb6c073a055ead6ce1c4bbe396cda + 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 - + https://github.com/dotnet/dotnet - 5507d7a2f05bb6c073a055ead6ce1c4bbe396cda + 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 - + https://github.com/dotnet/dotnet - 5507d7a2f05bb6c073a055ead6ce1c4bbe396cda + 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 - + https://github.com/dotnet/dotnet - 5507d7a2f05bb6c073a055ead6ce1c4bbe396cda + 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 - + https://github.com/dotnet/dotnet - 5507d7a2f05bb6c073a055ead6ce1c4bbe396cda + 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 - + https://github.com/dotnet/dotnet - 5507d7a2f05bb6c073a055ead6ce1c4bbe396cda + 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 - + https://github.com/dotnet/dotnet - 5507d7a2f05bb6c073a055ead6ce1c4bbe396cda + 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 https://github.com/dotnet/xharness @@ -299,9 +299,9 @@ https://github.com/dotnet/xharness 31e0b8e08f57890f7b7004b93361d69cd4b21079 - + https://github.com/dotnet/dotnet - 5507d7a2f05bb6c073a055ead6ce1c4bbe396cda + 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 https://dev.azure.com/dnceng/internal/_git/dotnet-optimization @@ -327,29 +327,29 @@ https://github.com/dotnet/runtime-assets 027a725187a44e48327b7f8a0e181b9b9c036ae7 - + https://github.com/dotnet/dotnet - 5507d7a2f05bb6c073a055ead6ce1c4bbe396cda + 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 - + https://github.com/dotnet/dotnet - 5507d7a2f05bb6c073a055ead6ce1c4bbe396cda + 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 - + https://github.com/dotnet/dotnet - 5507d7a2f05bb6c073a055ead6ce1c4bbe396cda + 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 - + https://github.com/dotnet/dotnet - 5507d7a2f05bb6c073a055ead6ce1c4bbe396cda + 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 - + https://github.com/dotnet/dotnet - 5507d7a2f05bb6c073a055ead6ce1c4bbe396cda + 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 - + https://github.com/dotnet/dotnet - 5507d7a2f05bb6c073a055ead6ce1c4bbe396cda + 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 https://dev.azure.com/dnceng/internal/_git/dotnet-optimization @@ -361,53 +361,53 @@ - + https://github.com/dotnet/dotnet - 5507d7a2f05bb6c073a055ead6ce1c4bbe396cda + 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 - + https://github.com/dotnet/dotnet - 5507d7a2f05bb6c073a055ead6ce1c4bbe396cda + 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 - + https://github.com/dotnet/dotnet - 5507d7a2f05bb6c073a055ead6ce1c4bbe396cda + 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 - + https://github.com/dotnet/dotnet - 5507d7a2f05bb6c073a055ead6ce1c4bbe396cda + 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 - + https://github.com/dotnet/node - 7ceff14f32d49de06cc7a2269c0f45fcb5364c84 + ccd4ce0d5f2660b91cc3237d58a1491c937321e1 - + https://github.com/dotnet/node - 7ceff14f32d49de06cc7a2269c0f45fcb5364c84 + ccd4ce0d5f2660b91cc3237d58a1491c937321e1 - + https://github.com/dotnet/node - 7ceff14f32d49de06cc7a2269c0f45fcb5364c84 + ccd4ce0d5f2660b91cc3237d58a1491c937321e1 - + https://github.com/dotnet/node - 7ceff14f32d49de06cc7a2269c0f45fcb5364c84 + ccd4ce0d5f2660b91cc3237d58a1491c937321e1 - + https://github.com/dotnet/node - 7ceff14f32d49de06cc7a2269c0f45fcb5364c84 + ccd4ce0d5f2660b91cc3237d58a1491c937321e1 - + https://github.com/dotnet/node - 7ceff14f32d49de06cc7a2269c0f45fcb5364c84 + ccd4ce0d5f2660b91cc3237d58a1491c937321e1 - + https://github.com/dotnet/node - 7ceff14f32d49de06cc7a2269c0f45fcb5364c84 + ccd4ce0d5f2660b91cc3237d58a1491c937321e1 - + https://github.com/dotnet/node - 7ceff14f32d49de06cc7a2269c0f45fcb5364c84 + ccd4ce0d5f2660b91cc3237d58a1491c937321e1 https://github.com/dotnet/runtime-assets diff --git a/eng/common/core-templates/job/renovate.yml b/eng/common/core-templates/job/renovate.yml new file mode 100644 index 00000000000000..b662269d5db9a3 --- /dev/null +++ b/eng/common/core-templates/job/renovate.yml @@ -0,0 +1,135 @@ +# -------------------------------------------------------------------------------------- +# Renovate Bot Job Template +# -------------------------------------------------------------------------------------- +# This Azure DevOps pipeline job template runs Renovate (https://docs.renovatebot.com/) +# to automatically update dependencies in a GitHub repository. +# +# Renovate scans the repository for dependency files and creates pull requests to update +# outdated dependencies based on the configuration specified in the renovateConfigPath +# parameter. +# +# Usage: +# For each product repo wanting to make use of Renovate, this template is called from +# an internal Azure DevOps pipeline, typically with a schedule trigger, to check for +# and propose dependency updates. +# +# For more info, see https://github.com/dotnet/arcade/blob/main/Documentation/Renovate.md +# -------------------------------------------------------------------------------------- + +parameters: + +# Path to the Renovate configuration file within the repository. +- name: renovateConfigPath + type: string + default: 'eng/renovate.json' + +# GitHub repository to run Renovate against, in the format 'owner/repo'. +# This could technically be any repo but convention is to target the same +# repo that contains the calling pipeline. The Renovate config file would +# be co-located with the pipeline's repo and, in most cases, the config +# file is specific to the repo being targeted. +- name: gitHubRepo + type: string + +# List of base branches to target for Renovate PRs. +# NOTE: The Renovate configuration file is always read from the branch where the +# pipeline is run, NOT from the target branches specified here. If you need different +# configurations for different branches, run the pipeline from each branch separately. +- name: baseBranches + type: object + default: + - main + +# When true, Renovate will run in dry run mode, which previews changes without creating PRs. +# See the 'Run Renovate' step log output for details of what would have been changed. +- name: dryRun + type: boolean + default: false + +# By default, Renovate will not recreate a PR for a given dependency/version pair that was +# previously closed. This allows opting in to always recreating PRs even if they were +# previously closed. +- name: forceRecreatePR + type: boolean + default: false + +# Pool configuration for the job. +- name: pool + type: object + default: + name: NetCore1ESPool-Internal + image: build.azurelinux.3.amd64 + os: linux + +jobs: +- job: Renovate + displayName: Run Renovate + container: RenovateContainer + variables: + - group: dotnet-renovate-bot + # The Renovate version is automatically updated by https://github.com/dotnet/arcade/blob/main/azure-pipelines-renovate.yml. + # Changing the variable name here would require updating the name in https://github.com/dotnet/arcade/blob/main/eng/renovate.json as well. + - name: renovateVersion + value: '42' + - name: dryRunArg + ${{ if eq(parameters.dryRun, true) }}: + value: 'full' + ${{ else }}: + value: '' + - name: recreateWhenArg + ${{ if eq(parameters.forceRecreatePR, true) }}: + value: 'always' + ${{ else }}: + value: '' + pool: ${{ parameters.pool }} + + templateContext: + outputParentDirectory: $(Build.ArtifactStagingDirectory) + outputs: + - output: pipelineArtifact + displayName: Publish Renovate Log + condition: succeededOrFailed() + targetPath: $(Build.ArtifactStagingDirectory) + artifactName: $(Agent.JobName)_Logs_Attempt$(System.JobAttempt) + sbomEnabled: false + + steps: + - checkout: self + fetchDepth: 1 + + - script: renovate-config-validator $(Build.SourcesDirectory)/${{parameters.renovateConfigPath}} + displayName: Validate Renovate config + env: + LOG_LEVEL: info + LOG_FILE_LEVEL: debug + LOG_FILE: $(Build.ArtifactStagingDirectory)/renovate-config-validator.json + + - script: | + . $(Build.SourcesDirectory)/eng/common/renovate.env + renovate + displayName: Run Renovate + env: + RENOVATE_FORK_TOKEN: $(BotAccount-dotnet-renovate-bot-PAT) + RENOVATE_TOKEN: $(BotAccount-dotnet-renovate-bot-PAT) + RENOVATE_REPOSITORIES: ${{parameters.gitHubRepo}} + RENOVATE_BASE_BRANCHES: ${{ convertToJson(parameters.baseBranches) }} + RENOVATE_DRY_RUN: $(dryRunArg) + RENOVATE_RECREATE_WHEN: $(recreateWhenArg) + LOG_LEVEL: info + LOG_FILE_LEVEL: debug + LOG_FILE: $(Build.ArtifactStagingDirectory)/renovate.json + RENOVATE_CONFIG_FILE: $(Build.SourcesDirectory)/${{parameters.renovateConfigPath}} + + - script: | + echo "PRs created by Renovate:" + if [ -s "$(Build.ArtifactStagingDirectory)/renovate-log.json" ]; then + if ! jq -r 'select(.msg == "PR created" and .pr != null) | "https://github.com/\(.repository)/pull/\(.pr)"' "$(Build.ArtifactStagingDirectory)/renovate-log.json" | sort -u; then + echo "##vso[task.logissue type=warning]Failed to parse Renovate log file with jq." + echo "##vso[task.complete result=SucceededWithIssues]" + fi + else + echo "##vso[task.logissue type=warning]No Renovate log file found or file is empty." + echo "##vso[task.complete result=SucceededWithIssues]" + fi + displayName: List created PRs + condition: and(succeededOrFailed(), eq('${{ parameters.dryRun }}', false)) diff --git a/eng/common/core-templates/job/source-index-stage1.yml b/eng/common/core-templates/job/source-index-stage1.yml index 76baf5c27258fa..cf02b82d4e2075 100644 --- a/eng/common/core-templates/job/source-index-stage1.yml +++ b/eng/common/core-templates/job/source-index-stage1.yml @@ -25,10 +25,10 @@ jobs: pool: ${{ if eq(variables['System.TeamProject'], 'public') }}: name: $(DncEngPublicBuildPool) - image: windows.vs2026preview.scout.amd64.open + image: windows.vs2026.amd64.open ${{ if eq(variables['System.TeamProject'], 'internal') }}: name: $(DncEngInternalBuildPool) - image: windows.vs2026preview.scout.amd64 + image: windows.vs2026.amd64 steps: - ${{ if eq(parameters.is1ESPipeline, '') }}: diff --git a/eng/common/core-templates/post-build/post-build.yml b/eng/common/core-templates/post-build/post-build.yml index 3bed9cdb49d87e..9438429ca3781e 100644 --- a/eng/common/core-templates/post-build/post-build.yml +++ b/eng/common/core-templates/post-build/post-build.yml @@ -127,11 +127,11 @@ stages: ${{ else }}: ${{ if eq(parameters.is1ESPipeline, true) }}: name: $(DncEngInternalBuildPool) - image: windows.vs2026preview.scout.amd64 + image: windows.vs2026.amd64 os: windows ${{ else }}: name: $(DncEngInternalBuildPool) - demands: ImageOverride -equals windows.vs2026preview.scout.amd64 + demands: ImageOverride -equals windows.vs2026.amd64 steps: - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml @@ -175,7 +175,7 @@ stages: os: windows ${{ else }}: name: $(DncEngInternalBuildPool) - demands: ImageOverride -equals windows.vs2026preview.scout.amd64 + demands: ImageOverride -equals windows.vs2026.amd64 steps: - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml parameters: @@ -236,7 +236,7 @@ stages: os: windows ${{ else }}: name: $(DncEngInternalBuildPool) - demands: ImageOverride -equals windows.vs2026preview.scout.amd64 + demands: ImageOverride -equals windows.vs2026.amd64 steps: - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml parameters: diff --git a/eng/common/core-templates/stages/renovate.yml b/eng/common/core-templates/stages/renovate.yml new file mode 100644 index 00000000000000..383e397e5d3d0c --- /dev/null +++ b/eng/common/core-templates/stages/renovate.yml @@ -0,0 +1,86 @@ +# -------------------------------------------------------------------------------------- +# Renovate Pipeline Template +# -------------------------------------------------------------------------------------- +# This template provides a complete reusable pipeline definition for running Renovate +# in a 1ES Official pipeline. Pipelines can extend from this template and only need +# to pass the Renovate job parameters. +# +# For more info, see https://github.com/dotnet/arcade/blob/main/Documentation/Renovate.md +# -------------------------------------------------------------------------------------- + +parameters: + +# Path to the Renovate configuration file within the repository. +- name: renovateConfigPath + type: string + default: 'eng/renovate.json' + +# GitHub repository to run Renovate against, in the format 'owner/repo'. +- name: gitHubRepo + type: string + +# List of base branches to target for Renovate PRs. +- name: baseBranches + type: object + default: + - main + +# When true, Renovate will run in dry run mode. +- name: dryRun + type: boolean + default: false + +# When true, Renovate will recreate PRs even if they were previously closed. +- name: forceRecreatePR + type: boolean + default: false + +# Pool configuration for the pipeline. +- name: pool + type: object + default: + name: NetCore1ESPool-Internal + image: build.azurelinux.3.amd64 + os: linux + +# Renovate version used in the container image tag. +- name: renovateVersion + default: 43 + type: number + +# Pool configuration for SDL analysis. +- name: sdlPool + type: object + default: + name: NetCore1ESPool-Internal + image: 1es-windows-2022 + os: windows + +resources: + repositories: + - repository: 1ESPipelineTemplates + type: git + name: 1ESPipelineTemplates/1ESPipelineTemplates + ref: refs/tags/release + +extends: + template: v1/1ES.Official.PipelineTemplate.yml@1ESPipelineTemplates + parameters: + pool: ${{ parameters.pool }} + sdl: + sourceAnalysisPool: ${{ parameters.sdlPool }} + containers: + RenovateContainer: + image: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-renovate-${{ parameters.renovateVersion }}-amd64 + stages: + - stage: Renovate + displayName: Run Renovate + jobs: + - template: /eng/common/core-templates/job/renovate.yml@self + parameters: + renovateConfigPath: ${{ parameters.renovateConfigPath }} + gitHubRepo: ${{ parameters.gitHubRepo }} + baseBranches: ${{ parameters.baseBranches }} + dryRun: ${{ parameters.dryRun }} + forceRecreatePR: ${{ parameters.forceRecreatePR }} + pool: ${{ parameters.pool }} diff --git a/eng/common/core-templates/steps/publish-logs.yml b/eng/common/core-templates/steps/publish-logs.yml index 5a927b4c7bcbf2..a9ea99ba6aaa5c 100644 --- a/eng/common/core-templates/steps/publish-logs.yml +++ b/eng/common/core-templates/steps/publish-logs.yml @@ -31,7 +31,6 @@ steps: -runtimeSourceFeed https://ci.dot.net/internal -runtimeSourceFeedKey '$(dotnetbuilds-internal-container-read-token-base64)' '$(publishing-dnceng-devdiv-code-r-build-re)' - '$(MaestroAccessToken)' '$(dn-bot-all-orgs-artifact-feeds-rw)' '$(akams-client-id)' '$(microsoft-symbol-server-pat)' diff --git a/eng/common/cross/build-rootfs.sh b/eng/common/cross/build-rootfs.sh index 205463aa51ebc4..abbb851415445a 100755 --- a/eng/common/cross/build-rootfs.sh +++ b/eng/common/cross/build-rootfs.sh @@ -622,9 +622,9 @@ elif [[ "$__CodeName" == "openbsd" ]]; then FILE="${set}${__OpenBSDVersion//./}.tgz" echo "Downloading $FILE..." if [[ "$__hasWget" == 1 ]]; then - wget -O- "$OPENBSD_MIRROR/$FILE" | tar -C "$ROOTFS" -xzpf - + wget -O- "$OPENBSD_MIRROR/$FILE" | tar -C "$__RootfsDir" -xzpf - else - curl -SL "$OPENBSD_MIRROR/$FILE" | tar -C "$ROOTFS" -xzpf - + curl -SL "$OPENBSD_MIRROR/$FILE" | tar -C "$__RootfsDir" -xzpf - fi done @@ -638,11 +638,11 @@ elif [[ "$__CodeName" == "openbsd" ]]; then if [[ "$__hasWget" == 1 ]]; then PKG_FILE=$(wget -qO- "$PKG_MIRROR/" | grep -Eo "${pkg}-[0-9][^\" ]*\.tgz" | head -n1) [[ -z "$PKG_FILE" ]] && { echo "ERROR: Package $pkg not found"; exit 1; } - wget -O- "$PKG_MIRROR/$PKG_FILE" | tar -C "$ROOTFS" -xzpf - + wget -O- "$PKG_MIRROR/$PKG_FILE" | tar -C "$__RootfsDir" -xzpf - else PKG_FILE=$(curl -s "$PKG_MIRROR/" | grep -Eo "${pkg}-[0-9][^\" ]*\.tgz" | head -n1) [[ -z "$PKG_FILE" ]] && { echo "ERROR: Package $pkg not found"; exit 1; } - curl -SL "$PKG_MIRROR/$PKG_FILE" | tar -C "$ROOTFS" -xzpf - + curl -SL "$PKG_MIRROR/$PKG_FILE" | tar -C "$__RootfsDir" -xzpf - fi done elif [[ "$__CodeName" == "illumos" ]]; then diff --git a/eng/common/renovate.env b/eng/common/renovate.env new file mode 100644 index 00000000000000..9f79dbc6b5fab5 --- /dev/null +++ b/eng/common/renovate.env @@ -0,0 +1,39 @@ +# Renovate Global Configuration +# https://docs.renovatebot.com/self-hosted-configuration/ +# +# NOTE: This file uses bash/shell format and is sourced via `. renovate.env`. +# Values containing spaces or special characters must be quoted. + +# Author to use for git commits made by Renovate +# https://docs.renovatebot.com/configuration-options/#gitauthor +export RENOVATE_GIT_AUTHOR='.NET Renovate ' + +# Disable rate limiting for PR creation (0 = unlimited) +# https://docs.renovatebot.com/presets-default/#prhourlylimitnone +# https://docs.renovatebot.com/presets-default/#prconcurrentlimitnone +export RENOVATE_PR_HOURLY_LIMIT=0 +export RENOVATE_PR_CONCURRENT_LIMIT=0 + +# Skip the onboarding PR that Renovate normally creates for new repos +# https://docs.renovatebot.com/config-overview/#onboarding +export RENOVATE_ONBOARDING=false + +# Any Renovate config file in the cloned repository is ignored. Only +# the Renovate config file from the repo where the pipeline is running +# is used (yes, those are the same repo but the sources may be different). +# https://docs.renovatebot.com/self-hosted-configuration/#requireconfig +export RENOVATE_REQUIRE_CONFIG=ignored + +# Customize the PR body content. This removes some of the default +# sections that aren't relevant in a self-hosted config. +# https://docs.renovatebot.com/configuration-options/#prheader +# https://docs.renovatebot.com/configuration-options/#prbodynotes +# https://docs.renovatebot.com/configuration-options/#prbodytemplate +export RENOVATE_PR_HEADER='## Automated Dependency Update' +export RENOVATE_PR_BODY_NOTES='["This PR has been created automatically by the [.NET Renovate Bot](https://github.com/dotnet/arcade/blob/main/Documentation/Renovate.md) to update one or more dependencies in your repo. Please review the changes and merge the PR if everything looks good."]' +export RENOVATE_PR_BODY_TEMPLATE='{{{header}}}{{{table}}}{{{warnings}}}{{{notes}}}{{{changelogs}}}' + +# Extend the global config with additional presets +# https://docs.renovatebot.com/self-hosted-configuration/#globalextends +# Disable the Dependency Dashboard issue that tracks all updates +export RENOVATE_GLOBAL_EXTENDS='[":disableDependencyDashboard"]' diff --git a/global.json b/global.json index 7da983676f1937..93744d9f88672b 100644 --- a/global.json +++ b/global.json @@ -1,18 +1,18 @@ { "sdk": { - "version": "11.0.100-preview.1.26104.118", + "version": "11.0.100-preview.3.26128.104", "allowPrerelease": true, "rollForward": "major" }, "tools": { - "dotnet": "11.0.100-preview.1.26104.118" + "dotnet": "11.0.100-preview.3.26128.104" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "11.0.0-beta.26152.106", - "Microsoft.DotNet.Helix.Sdk": "11.0.0-beta.26152.106", - "Microsoft.DotNet.SharedFramework.Sdk": "11.0.0-beta.26152.106", + "Microsoft.DotNet.Arcade.Sdk": "11.0.0-beta.26160.107", + "Microsoft.DotNet.Helix.Sdk": "11.0.0-beta.26160.107", + "Microsoft.DotNet.SharedFramework.Sdk": "11.0.0-beta.26160.107", "Microsoft.Build.NoTargets": "3.7.0", "Microsoft.Build.Traversal": "3.4.0", - "Microsoft.NET.Sdk.IL": "11.0.0-preview.3.26152.106" + "Microsoft.NET.Sdk.IL": "11.0.0-preview.3.26160.107" } } From bc3e5014883f9dde6e9ec7b20777118df098b75b Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Wed, 11 Mar 2026 02:04:10 +0000 Subject: [PATCH 3/5] Backflow from https://github.com/dotnet/dotnet / 542fea6 build 305563 [[ commit created by automation ]] --- eng/Signing.props | 1 + 1 file changed, 1 insertion(+) diff --git a/eng/Signing.props b/eng/Signing.props index 6a7c3c05e55256..1b36027d8f8ac9 100644 --- a/eng/Signing.props +++ b/eng/Signing.props @@ -54,6 +54,7 @@ + From e5b85e7215fec875f0b561cd2089fdcb39c78be4 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Wed, 11 Mar 2026 02:04:12 +0000 Subject: [PATCH 4/5] Update dependencies from build 305563 Updated Dependencies: Microsoft.CodeAnalysis, Microsoft.CodeAnalysis.Analyzers, Microsoft.CodeAnalysis.CSharp, Microsoft.Net.Compilers.Toolset (Version 5.6.0-2.26160.107 -> 5.6.0-2.26160.112) Microsoft.CodeAnalysis.NetAnalyzers, Microsoft.DotNet.ApiCompat.Task, Microsoft.NET.Workload.Emscripten.Current.Manifest-11.0.100.Transport (Version 11.0.100-preview.3.26160.107 -> 11.0.100-preview.3.26160.112) Microsoft.DotNet.Arcade.Sdk, Microsoft.DotNet.Build.Tasks.Archives, Microsoft.DotNet.Build.Tasks.Feed, Microsoft.DotNet.Build.Tasks.Installers, Microsoft.DotNet.Build.Tasks.Packaging, Microsoft.DotNet.Build.Tasks.TargetFramework, Microsoft.DotNet.Build.Tasks.Templating, Microsoft.DotNet.Build.Tasks.Workloads, Microsoft.DotNet.CodeAnalysis, Microsoft.DotNet.GenAPI, Microsoft.DotNet.GenFacades, Microsoft.DotNet.Helix.Sdk, Microsoft.DotNet.PackageTesting, Microsoft.DotNet.RemoteExecutor, Microsoft.DotNet.SharedFramework.Sdk, Microsoft.DotNet.XliffTasks, Microsoft.DotNet.XUnitExtensions (Version 11.0.0-beta.26160.107 -> 11.0.0-beta.26160.112) Microsoft.DotNet.Cecil (Version 0.11.5-preview.26160.107 -> 0.11.5-preview.26160.112) Microsoft.DotNet.XUnitAssert, Microsoft.DotNet.XUnitConsoleRunner (Version 2.9.3-beta.26160.107 -> 2.9.3-beta.26160.112) Microsoft.NET.Sdk.IL, Microsoft.NETCore.App.Ref, Microsoft.NETCore.ILAsm, runtime.native.System.IO.Ports, System.Reflection.Metadata, System.Reflection.MetadataLoadContext, System.Text.Json (Version 11.0.0-preview.3.26160.107 -> 11.0.0-preview.3.26160.112) NuGet.Frameworks, NuGet.Packaging, NuGet.ProjectModel, NuGet.Versioning (Version 7.5.0-rc.16107 -> 7.5.0-rc.16112) System.CommandLine (Version 3.0.0-preview.3.26160.107 -> 3.0.0-preview.3.26160.112) [[ commit created by automation ]] --- eng/Version.Details.props | 78 +++++++++---------- eng/Version.Details.xml | 158 +++++++++++++++++++------------------- global.json | 8 +- 3 files changed, 122 insertions(+), 122 deletions(-) diff --git a/eng/Version.Details.props b/eng/Version.Details.props index 1f34138b21f086..f69559c4dbf965 100644 --- a/eng/Version.Details.props +++ b/eng/Version.Details.props @@ -6,45 +6,45 @@ This file should be imported by eng/Versions.props - 5.6.0-2.26160.107 - 5.6.0-2.26160.107 - 5.6.0-2.26160.107 - 11.0.100-preview.3.26160.107 - 11.0.100-preview.3.26160.107 - 11.0.0-beta.26160.107 - 11.0.0-beta.26160.107 - 11.0.0-beta.26160.107 - 11.0.0-beta.26160.107 - 11.0.0-beta.26160.107 - 11.0.0-beta.26160.107 - 11.0.0-beta.26160.107 - 11.0.0-beta.26160.107 - 0.11.5-preview.26160.107 - 11.0.0-beta.26160.107 - 11.0.0-beta.26160.107 - 11.0.0-beta.26160.107 - 11.0.0-beta.26160.107 - 11.0.0-beta.26160.107 - 11.0.0-beta.26160.107 - 11.0.0-beta.26160.107 - 11.0.0-beta.26160.107 - 2.9.3-beta.26160.107 - 2.9.3-beta.26160.107 - 11.0.0-beta.26160.107 - 5.6.0-2.26160.107 - 11.0.0-preview.3.26160.107 - 11.0.100-preview.3.26160.107 - 11.0.0-preview.3.26160.107 - 11.0.0-preview.3.26160.107 - 7.5.0-rc.16107 - 7.5.0-rc.16107 - 7.5.0-rc.16107 - 7.5.0-rc.16107 - 11.0.0-preview.3.26160.107 - 3.0.0-preview.3.26160.107 - 11.0.0-preview.3.26160.107 - 11.0.0-preview.3.26160.107 - 11.0.0-preview.3.26160.107 + 5.6.0-2.26160.112 + 5.6.0-2.26160.112 + 5.6.0-2.26160.112 + 11.0.100-preview.3.26160.112 + 11.0.100-preview.3.26160.112 + 11.0.0-beta.26160.112 + 11.0.0-beta.26160.112 + 11.0.0-beta.26160.112 + 11.0.0-beta.26160.112 + 11.0.0-beta.26160.112 + 11.0.0-beta.26160.112 + 11.0.0-beta.26160.112 + 11.0.0-beta.26160.112 + 0.11.5-preview.26160.112 + 11.0.0-beta.26160.112 + 11.0.0-beta.26160.112 + 11.0.0-beta.26160.112 + 11.0.0-beta.26160.112 + 11.0.0-beta.26160.112 + 11.0.0-beta.26160.112 + 11.0.0-beta.26160.112 + 11.0.0-beta.26160.112 + 2.9.3-beta.26160.112 + 2.9.3-beta.26160.112 + 11.0.0-beta.26160.112 + 5.6.0-2.26160.112 + 11.0.0-preview.3.26160.112 + 11.0.100-preview.3.26160.112 + 11.0.0-preview.3.26160.112 + 11.0.0-preview.3.26160.112 + 7.5.0-rc.16112 + 7.5.0-rc.16112 + 7.5.0-rc.16112 + 7.5.0-rc.16112 + 11.0.0-preview.3.26160.112 + 3.0.0-preview.3.26160.112 + 11.0.0-preview.3.26160.112 + 11.0.0-preview.3.26160.112 + 11.0.0-preview.3.26160.112 11.0.0-alpha.0.25625.1 diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index ea0fb5fcf07f32..ac48af7b078099 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,5 +1,5 @@ - + https://github.com/dotnet/icu @@ -37,91 +37,91 @@ https://github.com/dotnet/llvm-project 07cf3afbd65f821dc9aec958aab04fa37fdce2ec - + https://github.com/dotnet/dotnet - 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 + 542fea6988c173c64859bab06c9d2d100034843c - + https://github.com/dotnet/dotnet - 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 + 542fea6988c173c64859bab06c9d2d100034843c - + https://github.com/dotnet/dotnet - 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 + 542fea6988c173c64859bab06c9d2d100034843c - + https://github.com/dotnet/dotnet - 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 + 542fea6988c173c64859bab06c9d2d100034843c - + https://github.com/dotnet/dotnet - 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 + 542fea6988c173c64859bab06c9d2d100034843c - + https://github.com/dotnet/dotnet - 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 + 542fea6988c173c64859bab06c9d2d100034843c - + https://github.com/dotnet/dotnet - 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 + 542fea6988c173c64859bab06c9d2d100034843c - + https://github.com/dotnet/dotnet - 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 + 542fea6988c173c64859bab06c9d2d100034843c - + https://github.com/dotnet/dotnet - 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 + 542fea6988c173c64859bab06c9d2d100034843c - + https://github.com/dotnet/dotnet - 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 + 542fea6988c173c64859bab06c9d2d100034843c - + https://github.com/dotnet/dotnet - 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 + 542fea6988c173c64859bab06c9d2d100034843c - + https://github.com/dotnet/dotnet - 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 + 542fea6988c173c64859bab06c9d2d100034843c - + https://github.com/dotnet/dotnet - 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 + 542fea6988c173c64859bab06c9d2d100034843c - + https://github.com/dotnet/dotnet - 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 + 542fea6988c173c64859bab06c9d2d100034843c - + https://github.com/dotnet/dotnet - 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 + 542fea6988c173c64859bab06c9d2d100034843c - + https://github.com/dotnet/dotnet - 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 + 542fea6988c173c64859bab06c9d2d100034843c - + https://github.com/dotnet/dotnet - 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 + 542fea6988c173c64859bab06c9d2d100034843c - + https://github.com/dotnet/dotnet - 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 + 542fea6988c173c64859bab06c9d2d100034843c - + https://github.com/dotnet/dotnet - 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 + 542fea6988c173c64859bab06c9d2d100034843c - + https://github.com/dotnet/dotnet - 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 + 542fea6988c173c64859bab06c9d2d100034843c - + https://github.com/dotnet/dotnet - 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 + 542fea6988c173c64859bab06c9d2d100034843c https://github.com/dotnet/runtime-assets @@ -259,33 +259,33 @@ https://github.com/dotnet/llvm-project 07cf3afbd65f821dc9aec958aab04fa37fdce2ec - + https://github.com/dotnet/dotnet - 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 + 542fea6988c173c64859bab06c9d2d100034843c - + https://github.com/dotnet/dotnet - 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 + 542fea6988c173c64859bab06c9d2d100034843c - + https://github.com/dotnet/dotnet - 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 + 542fea6988c173c64859bab06c9d2d100034843c - + https://github.com/dotnet/dotnet - 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 + 542fea6988c173c64859bab06c9d2d100034843c - + https://github.com/dotnet/dotnet - 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 + 542fea6988c173c64859bab06c9d2d100034843c - + https://github.com/dotnet/dotnet - 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 + 542fea6988c173c64859bab06c9d2d100034843c - + https://github.com/dotnet/dotnet - 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 + 542fea6988c173c64859bab06c9d2d100034843c https://github.com/dotnet/xharness @@ -299,9 +299,9 @@ https://github.com/dotnet/xharness 31e0b8e08f57890f7b7004b93361d69cd4b21079 - + https://github.com/dotnet/dotnet - 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 + 542fea6988c173c64859bab06c9d2d100034843c https://dev.azure.com/dnceng/internal/_git/dotnet-optimization @@ -327,29 +327,29 @@ https://github.com/dotnet/runtime-assets 027a725187a44e48327b7f8a0e181b9b9c036ae7 - + https://github.com/dotnet/dotnet - 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 + 542fea6988c173c64859bab06c9d2d100034843c - + https://github.com/dotnet/dotnet - 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 + 542fea6988c173c64859bab06c9d2d100034843c - + https://github.com/dotnet/dotnet - 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 + 542fea6988c173c64859bab06c9d2d100034843c - + https://github.com/dotnet/dotnet - 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 + 542fea6988c173c64859bab06c9d2d100034843c - + https://github.com/dotnet/dotnet - 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 + 542fea6988c173c64859bab06c9d2d100034843c - + https://github.com/dotnet/dotnet - 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 + 542fea6988c173c64859bab06c9d2d100034843c https://dev.azure.com/dnceng/internal/_git/dotnet-optimization @@ -361,21 +361,21 @@ - + https://github.com/dotnet/dotnet - 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 + 542fea6988c173c64859bab06c9d2d100034843c - + https://github.com/dotnet/dotnet - 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 + 542fea6988c173c64859bab06c9d2d100034843c - + https://github.com/dotnet/dotnet - 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 + 542fea6988c173c64859bab06c9d2d100034843c - + https://github.com/dotnet/dotnet - 349974995b66d78aa8f24e5e9ce9fa3a3430cd05 + 542fea6988c173c64859bab06c9d2d100034843c https://github.com/dotnet/node diff --git a/global.json b/global.json index 93744d9f88672b..6c24d95c0c1c9a 100644 --- a/global.json +++ b/global.json @@ -8,11 +8,11 @@ "dotnet": "11.0.100-preview.3.26128.104" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "11.0.0-beta.26160.107", - "Microsoft.DotNet.Helix.Sdk": "11.0.0-beta.26160.107", - "Microsoft.DotNet.SharedFramework.Sdk": "11.0.0-beta.26160.107", + "Microsoft.DotNet.Arcade.Sdk": "11.0.0-beta.26160.112", + "Microsoft.DotNet.Helix.Sdk": "11.0.0-beta.26160.112", + "Microsoft.DotNet.SharedFramework.Sdk": "11.0.0-beta.26160.112", "Microsoft.Build.NoTargets": "3.7.0", "Microsoft.Build.Traversal": "3.4.0", - "Microsoft.NET.Sdk.IL": "11.0.0-preview.3.26160.107" + "Microsoft.NET.Sdk.IL": "11.0.0-preview.3.26160.112" } } From e1798c3f95d084275f81f9fa8a92b36ffc22a697 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Fri, 13 Mar 2026 17:24:26 -0500 Subject: [PATCH 5/5] Backflow from https://github.com/dotnet/dotnet / 08e21da build 306112 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .editorconfig | 4 + .github/copilot-instructions.md | 11 +- THIRD-PARTY-NOTICES.TXT | 33 + docs/design/coreclr/botr/clr-abi.md | 1 + docs/design/datacontracts/BuiltInCOM.md | 56 +- docs/design/datacontracts/ExecutionManager.md | 70 +- docs/design/datacontracts/Loader.md | 8 +- eng/Version.Details.props | 84 +-- eng/Version.Details.xml | 170 ++--- .../core-templates/post-build/post-build.yml | 2 +- eng/native.wasm.targets | 19 +- .../runtime-extra-platforms-ioslike.yml | 2 +- ...ntime-extra-platforms-ioslikesimulator.yml | 2 +- .../runtime-extra-platforms-maccatalyst.yml | 4 +- eng/testing/BrowserVersions.props | 2 +- eng/testing/tests.targets | 1 - eng/testing/xunit/xunit.props | 3 + global.json | 12 +- .../Runtime/InteropServices/ComActivator.cs | 39 +- .../src/System/RuntimeType.CoreCLR.cs | 33 + .../src/System/StubHelpers.cs | 14 + .../src/System/Variant.cs | 39 + .../src/System/__ComObject.cs | 14 + src/coreclr/clr.featuredefines.props | 6 +- src/coreclr/clrdefinitions.cmake | 6 +- src/coreclr/debug/daccess/dacdbiimpl.cpp | 10 + src/coreclr/debug/daccess/request.cpp | 25 +- src/coreclr/inc/eexcp.h | 2 +- src/coreclr/inc/readytorun.h | 2 +- src/coreclr/interpreter/compiler.cpp | 2 - src/coreclr/jit/codegen.h | 7 +- src/coreclr/jit/codegenlinear.cpp | 10 + src/coreclr/jit/codegenloongarch64.cpp | 96 +-- src/coreclr/jit/codegenwasm.cpp | 25 +- src/coreclr/jit/compiler.cpp | 5 + src/coreclr/jit/compiler.h | 16 + src/coreclr/jit/compiler.hpp | 61 +- src/coreclr/jit/emit.cpp | 5 +- src/coreclr/jit/emitloongarch64.cpp | 31 +- src/coreclr/jit/gentree.cpp | 3 - src/coreclr/jit/hwintrinsiclistarm64sve.h | 8 +- src/coreclr/jit/ifconversion.cpp | 100 +-- src/coreclr/jit/importer.cpp | 5 +- src/coreclr/jit/lclmorph.cpp | 55 +- src/coreclr/jit/lclvars.cpp | 8 +- src/coreclr/jit/lower.cpp | 28 +- src/coreclr/jit/morph.cpp | 70 +- src/coreclr/jit/utils.h | 3 +- .../nativeaot/Runtime/loongarch64/GcProbe.S | 37 +- .../Runtime/unix/unixasmmacrosloongarch64.inc | 1 + src/coreclr/pal/src/CMakeLists.txt | 6 +- src/coreclr/pal/src/synchmgr/synchmanager.cpp | 6 +- src/coreclr/pal/src/synchmgr/wait.cpp | 12 +- src/coreclr/pal/src/thread/thread.cpp | 10 +- src/coreclr/pal/src/thread/threadsusp.cpp | 10 +- .../miscellaneous/CloseHandle/test1/test.cpp | 30 +- src/coreclr/scripts/superpmi.py | 3 +- .../Compiler/CompilerTypeSystemContext.cs | 1 + .../DependencyAnalysis/AssemblyStubNode.cs | 3 + .../INodeWithTypeSignature.cs | 18 + .../Compiler/DependencyAnalysis/ObjectNode.cs | 8 + .../Compiler/DependencyAnalysis/Relocation.cs | 6 +- .../Target_Wasm/WasmTypes.cs | 1 + .../{Target_Wasm => }/WasmTypeNode.cs | 5 +- .../Compiler/ObjectWriter/ObjectWriter.cs | 31 +- .../Compiler/ObjectWriter/PEObjectWriter.cs | 7 +- .../Compiler/ObjectWriter/WasmNative.cs | 1 + .../Compiler/ObjectWriter/WasmObjectWriter.cs | 13 +- .../tools/Common/JitInterface/WasmLowering.cs | 2 + .../Compiler/CompilerTypeSystemContext.Aot.cs | 2 - .../DependencyAnalysis/NodeFactory.cs | 17 + .../ILCompiler.Compiler.csproj | 3 +- .../ReadyToRun/DelayLoadHelperMethodImport.cs | 2 +- .../ReadyToRun/DelayLoadMethodImport.cs | 2 +- .../ReadyToRun/MethodWithGCInfo.cs | 2 +- .../ReadyToRun/Target_Wasm/ImportThunk.cs | 1 + .../ReadyToRunCodegenNodeFactory.cs | 8 + .../Compiler/ReadyToRunCompilerContext.cs | 3 +- .../ILCompiler.ReadyToRun.csproj | 3 +- .../JitInterface/CorInfoImpl.ReadyToRun.cs | 3 + .../DependencyAnalysis/MethodCodeNode.cs | 2 +- src/coreclr/utilcode/util.cpp | 4 + src/coreclr/vm/appdomain.cpp | 6 +- src/coreclr/vm/callstubgenerator.cpp | 4 +- src/coreclr/vm/callstubgenerator.h | 2 +- src/coreclr/vm/clrtocomcall.cpp | 41 +- src/coreclr/vm/comcache.h | 9 + src/coreclr/vm/corelib.h | 18 +- src/coreclr/vm/custommarshalerinfo.cpp | 4 +- .../vm/datadescriptor/datadescriptor.inc | 61 +- src/coreclr/vm/interpexec.cpp | 4 +- src/coreclr/vm/jitinterface.cpp | 15 +- src/coreclr/vm/loongarch64/asmhelpers.S | 12 +- src/coreclr/vm/loongarch64/cgencpu.h | 5 + src/coreclr/vm/loongarch64/stubs.cpp | 2 + src/coreclr/vm/metasig.h | 14 + src/coreclr/vm/olevariant.cpp | 24 +- src/coreclr/vm/prestub.cpp | 36 +- src/coreclr/vm/readytoruninfo.cpp | 28 +- src/coreclr/vm/readytoruninfo.h | 12 +- src/coreclr/vm/runtimecallablewrapper.cpp | 24 +- src/coreclr/vm/runtimecallablewrapper.h | 2 +- src/coreclr/vm/wasm/helpers.cpp | 4 +- .../Unix/System.Native/Interop.Pipe.cs | 2 + .../Unix/System.Native/Interop.Read.cs | 3 + .../Unix/System.Native/Interop.Write.cs | 3 + .../IpHlpApi/Interop.NetworkInformation.cs | 100 ++- .../Interop.CreateNamedPipe_SafeFileHandle.cs | 22 + .../src/SourceGenerators/DiagnosticInfo.cs | 60 -- .../Asn1/AlgorithmIdentifierAsn.manual.cs | 55 ++ .../Asn1/AlgorithmIdentifierAsn.xml | 3 +- .../Asn1/AlgorithmIdentifierAsn.xml.cs | 62 ++ .../Cryptography/Asn1/AttributeAsn.xml | 5 +- .../Cryptography/Asn1/AttributeAsn.xml.cs | 112 +++ .../Asn1/GeneralSubtreeAsn.xml.cs | 25 +- .../Cryptography/Asn1/OaepParamsAsn.xml.cs | 41 +- .../Cryptography/Asn1/Pbkdf2Params.xml.cs | 27 +- .../Cryptography/Asn1/Pkcs12/MacData.xml.cs | 25 +- .../Cryptography/Asn1/PssParamsAsn.manual.cs | 56 ++ .../Cryptography/Asn1/PssParamsAsn.xml | 21 +- .../Cryptography/Asn1/PssParamsAsn.xml.cs | 180 ++++- .../Asn1/SubjectPublicKeyInfoAsn.xml | 10 +- .../Asn1/SubjectPublicKeyInfoAsn.xml.cs | 65 ++ .../Cryptography/Asn1/X509ExtensionAsn.xml | 3 +- .../Cryptography/Asn1/X509ExtensionAsn.xml.cs | 103 ++- .../System/Security/Cryptography/Asn1/asn.xsd | 12 + .../Security/Cryptography/Asn1/asn.xslt | 491 ++++++++++++- .../Cryptography/Asn1Reader/AsnValueReader.cs | 258 ------- .../AsnWriter/AsnWriterExtensions.cs | 38 + ...y.Cryptography.AsnWriter.Shared.projitems} | 4 +- .../Security/Cryptography/RSAOpenSsl.cs | 47 +- .../System/Collections/ICollectionTest.cs | 6 +- .../CompositeMLDsaTestHelpers.cs | 4 +- .../src/Microsoft.Bcl.Cryptography.csproj | 2 +- .../Microsoft.Bcl.Cryptography.Tests.csproj | 2 +- .../ConfigurationBindingGenerator.Parser.cs | 10 +- .../gen/ConfigurationBindingGenerator.cs | 45 +- ...nfiguration.Binder.SourceGeneration.csproj | 1 - .../SourceGenerationTests/GeneratorTests.cs | 125 ++++ .../gen/LoggerMessageGenerator.Parser.cs | 10 +- .../gen/LoggerMessageGenerator.Roslyn4.0.cs | 127 ++-- ...soft.Extensions.Logging.Generators.targets | 1 - .../LoggerMessageGeneratorParserTests.cs | 31 + .../tests/Common/LoggerMessageTest.cs | 2 +- .../src/System.Diagnostics.Process.csproj | 7 +- .../src/System/Diagnostics/Process.Windows.cs | 81 +- .../tests/PipeStreamConformanceTests.cs | 24 + .../src/System.Linq.Parallel.csproj | 5 +- .../src/System.Net.Http.csproj | 2 - .../SystemIPGlobalProperties.cs | 155 ++-- .../NetworkInformation/SystemTcpConnection.cs | 24 +- .../src/System.Net.Security.csproj | 2 - .../NegotiateAuthenticationPal.ManagedNtlm.cs | 78 +- ...egotiateAuthenticationPal.ManagedSpnego.cs | 12 +- .../System/Net/Sockets/SendPacketsElement.cs | 4 +- .../FunctionalTests/SendPacketsElementTest.cs | 1 + .../src/System.Net.WebSockets.Client.csproj | 3 +- .../Numerics/Tensors/netcore/TensorShape.cs | 24 +- .../tests/ReadOnlyTensorSpanTests.cs | 2 +- .../tests/TensorPrimitives.Generic.cs | 2 +- .../TensorPrimitives.NonGeneric.Single.cs | 2 +- .../tests/TensorSpanTests.cs | 2 +- .../tests/TensorTests.cs | 71 ++ .../ref/System.Private.CoreLib.csproj | 3 +- .../Win32/SafeHandles/SafeFileHandle.Unix.cs | 72 +- .../SafeHandles/SafeFileHandle.Windows.cs | 69 ++ .../Win32/SafeHandles/SafeFileHandle.cs | 18 + .../System.Private.CoreLib.Shared.projitems | 36 +- .../src/System/IO/RandomAccess.Unix.cs | 12 +- .../CompilerServices/RuntimeFeature.cs | 16 +- .../Arm/Sve.PlatformNotSupported.cs | 124 ---- .../src/System/Runtime/Intrinsics/Arm/Sve.cs | 124 ---- .../System/Threading/ThreadPoolWorkQueue.cs | 12 +- ....Runtime.InteropServices.JavaScript.csproj | 9 +- ...me.InteropServices.JavaScript.Tests.csproj | 7 +- .../NFloatTests.GenericMath.cs | 2 +- .../ref/System.Runtime.Intrinsics.cs | 20 - .../tests/ComplexTests.GenericMath.cs | 2 +- .../System.Runtime/ref/System.Runtime.cs | 1 + .../CharUnicodeInfoTests.Generated.cs | 4 +- .../Directory/Delete_MountVolume.cs | 28 +- .../File/OpenHandle.cs | 33 +- .../FileStream/FileStreamConformanceTests.cs | 40 + .../FileStream/FileStreamOptions.cs | 2 +- .../FileStream/IsAsync.cs | 6 +- .../FileStream/ctor_sfh_fa_buffer_async.cs | 2 +- .../ctor_str_fm_fa_fs_buffer_async.cs | 2 +- .../FileStream/ctor_str_fm_fa_fs_buffer_fo.cs | 2 +- .../FileSystemTest.cs | 2 + .../System.Runtime.Tests/System/CharTests.cs | 20 +- .../System/DecimalTests.cs | 36 +- .../System/DoubleTests.GenericMath.cs | 2 +- .../System/HalfTests.GenericMath.cs | 2 +- .../System/Numerics/DimTests.GenericMath.cs | 2 +- .../System/SingleTests.GenericMath.cs | 2 +- .../System/Text/RuneTests.cs | 4 +- .../System.Security.Cryptography.Pkcs.csproj | 2 +- .../Cryptography/Pkcs/Asn1/EssCertIdV2.xml.cs | 25 +- .../Cryptography/Pkcs/Asn1/MessageImprint.xml | 3 +- .../Pkcs/Asn1/MessageImprint.xml.cs | 65 ++ .../Pkcs/Asn1/Rfc3161TimeStampReq.xml.cs | 31 +- .../Cryptography/Pkcs/Asn1/Rfc3161TstInfo.xml | 5 +- .../Pkcs/Asn1/Rfc3161TstInfo.xml.cs | 154 +++- .../src/System.Security.Cryptography.csproj | 2 +- .../Asn1/BasicConstraintsAsn.xml.cs | 23 +- .../Asn1/CertificationRequestInfoAsn.xml | 14 +- .../Asn1/CertificationRequestInfoAsn.xml.cs | 121 +++ .../Asn1/TbsCertificateAsn.xml.cs | 39 +- .../CertificateRequest.Load.cs | 219 +++--- .../X509Certificates/PublicKey.cs | 48 +- .../System.Security.Cryptography.Tests.csproj | 2 +- .../gen/JsonSourceGenerator.Parser.cs | 4 +- .../gen/JsonSourceGenerator.Roslyn3.11.cs | 4 +- .../gen/JsonSourceGenerator.Roslyn4.0.cs | 48 +- .../System.Text.Json.SourceGeneration.targets | 1 - .../JsonSourceGeneratorDiagnosticsTests.cs | 29 + .../JsonSourceGeneratorIncrementalTests.cs | 1 - .../gen/RegexGenerator.Parser.cs | 40 +- .../gen/RegexGenerator.cs | 132 ++-- .../FunctionalTests/GroupCollectionTests2.cs | 2 +- .../FunctionalTests/Regex.Match.Tests.cs | 5 + .../RegexGeneratorHelper.netcoreapp.cs | 2 +- .../RegexGeneratorParserTests.cs | 22 + .../System.Threading.Tasks.Parallel.csproj | 5 +- ...eading.Thread.WebAssembly.Threading.csproj | 3 +- ...ng.ThreadPool.WebAssembly.Threading.csproj | 3 +- .../ref/System.Threading.ThreadPool.csproj | 1 - ...tem.Threading.WebAssembly.Threading.csproj | 3 +- .../src/System.Threading.csproj | 3 +- ...iCompatBaseline.NetCoreAppLatestStable.xml | 96 +++ src/libraries/pretest.proj | 1 - src/libraries/sfx-finish.proj | 1 - .../System.Private.CoreLib.csproj | 9 +- .../wasm/Wasm.Build.Tests/Common/TestUtils.cs | 2 +- .../App/Layout/NavMenu.razor | 2 +- src/mono/wasm/threads.md | 8 +- .../libs/Common/JavaScript/loader/assets.ts | 33 +- .../libs/Common/JavaScript/loader/run.ts | 95 +-- src/native/libs/System.Native/entrypoints.c | 2 + .../System.Native/pal_interfaceaddresses.c | 20 +- src/native/libs/System.Native/pal_io.c | 107 ++- src/native/libs/System.Native/pal_io.h | 21 +- .../Contracts/IBuiltInCOM.cs | 2 + .../Contracts/IExecutionManager.cs | 24 + .../DataType.cs | 8 + .../Constants.cs | 1 + .../Contracts/BuiltInCOM_1.cs | 19 + .../ExecutionManagerCore.EEJitManager.cs | 20 + ...ecutionManagerCore.ReadyToRunJitManager.cs | 85 ++- .../ExecutionManager/ExecutionManagerCore.cs | 110 +++ .../ExecutionManager/ExecutionManager_1.cs | 2 + .../ExecutionManager/ExecutionManager_2.cs | 2 + .../Helpers/UnwindDataSize.cs | 13 +- .../Contracts/GCInfo/GCInfoFactory.cs | 2 + .../PlatformTraits/LoongArch64GCInfoTraits.cs | 43 ++ .../PlatformTraits/RISCV64GCInfoTraits.cs | 43 ++ .../Contracts/Loader_1.cs | 3 +- .../Context/IPlatformAgnosticContext.cs | 1 + .../LoongArch64/LoongArch64Unwinder.cs | 167 ++++- .../Context/RISCV64/RISCV64Unwinder.cs | 174 ++++- .../FrameHandling/RISCV64FrameHandler.cs | 9 + .../Data/EEILException.cs | 18 + .../Data/ExceptionClause.cs | 67 ++ .../Data/ExceptionLookupTableEntry.cs | 18 + .../Data/InterfaceEntry.cs | 19 + .../Data/RCW.cs | 14 + .../Data/ReadyToRunCoreHeader.cs | 28 + .../Data/ReadyToRunCoreInfo.cs | 19 + .../Data/ReadyToRunInfo.cs | 4 + .../Data/ReadyToRunSection.cs | 21 + .../Data/RealCodeHeader.cs | 3 + .../EcmaMetadataUtils.cs | 6 +- .../ClrDataAppDomain.cs | 130 +++- .../ClrDataFrame.cs | 92 ++- .../ClrDataTask.cs | 2 +- .../ISOSDacInterface.cs | 41 +- .../IXCLRData.cs | 4 +- .../SOSDacImpl.cs | 231 +++++- .../managed/cdac/tests/BuiltInCOMTests.cs | 211 ++++++ .../ExceptionHandlingInfo.csproj | 5 + .../ExceptionHandlingInfo/Program.cs | 50 ++ .../Debuggees/RCWInterfaces/Program.cs | 119 +++ .../RCWInterfaces/RCWInterfaces.csproj | 7 + .../DumpTests/Debuggees/StackWalk/Program.cs | 18 +- .../managed/cdac/tests/DumpTests/DumpInfo.cs | 2 + .../cdac/tests/DumpTests/DumpTestBase.cs | 16 +- .../ExceptionHandlingInfoDumpTests.cs | 172 +++++ .../DumpTests/IXCLRDataAppDomainDumpTests.cs | 218 ++++++ .../DumpTests/IXCLRDataFrameDumpTests.cs | 308 ++++++++ .../tests/DumpTests/RCWInterfacesDumpTests.cs | 75 ++ .../tests/DumpTests/RuntimeInfoDumpTests.cs | 2 + .../cdac/tests/GetRegisterNameTests.cs | 4 +- .../MockDescriptors.ExecutionManager.cs | 6 +- .../WasmAppBuilder/WasmAppBuilder.csproj | 29 +- .../coreclr/PInvokeTableGenerator.cs | 10 +- .../WasmAppBuilder/coreclr/SignatureMapper.cs | 2 +- .../GenerateHWIntrinsicTests/Arm/SveTests.cs | 18 - src/tests/Directory.Build.props | 2 +- .../Github/Runtime_76219/Runtime_76219.csproj | 3 + .../JIT/Methodical/Boxing/morph/sin3double.il | 6 - .../flowgraph/bug619534/moduleHandleCache.cs | 2 - src/tests/JIT/Methodical/switch/switch6.il | 7 - .../JitBlue/Runtime_125301/Runtime_125301.cs | 34 + .../JitBlue/Runtime_125327/Runtime_125327.cs | 66 ++ .../JIT/Regression/Regression_ro_1.csproj | 1 + .../JIT/Regression/Regression_ro_2.csproj | 1 + .../JIT/opt/InstructionCombining/Casts.cs | 694 ++++++++++++++++++ .../JIT/opt/InstructionCombining/Casts.csproj | 17 + src/tests/async/devirtualize/devirtualize.cs | 144 ++++ .../async/devirtualize/devirtualize.csproj | 5 + src/tests/nativeaot/Directory.Build.props | 6 - src/tests/nativeaot/Directory.Build.targets | 13 - .../MobileSmokeTest/MobileSmokeTest.cs | 28 + .../MobileSmokeTest/MobileSmokeTest.csproj | 14 + .../nativeaot/StartupHook/StartupHook.csproj | 2 +- src/tests/nativeaot/nativeaot.csproj | 7 + .../TestFrameworkTests.g.cs | 12 + .../Assertions/KeptAttributeAttribute.cs | 13 + .../Reflection/TypeMap.cs | 25 +- .../VerifyKeptAttributeAttributeWorks.cs | 310 ++++++++ .../TestFramework/VerifyLocalsAreChanged.cs | 44 ++ .../TestFramework/VerifyLocalsAreChanged.xml | 11 + .../TestCasesRunner/AssemblyChecker.cs | 170 ++++- 323 files changed, 8642 insertions(+), 2388 deletions(-) create mode 100644 src/coreclr/tools/Common/Compiler/DependencyAnalysis/INodeWithTypeSignature.cs rename src/coreclr/tools/Common/Compiler/DependencyAnalysis/{Target_Wasm => }/WasmTypeNode.cs (96%) create mode 100644 src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreateNamedPipe_SafeFileHandle.cs delete mode 100644 src/libraries/Common/src/SourceGenerators/DiagnosticInfo.cs delete mode 100644 src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnValueReader.cs create mode 100644 src/libraries/Common/src/System/Security/Cryptography/AsnWriter/AsnWriterExtensions.cs rename src/libraries/Common/src/System/Security/Cryptography/{Asn1Reader/System.Security.Cryptography.Asn1Reader.Shared.projitems => AsnWriter/System.Security.Cryptography.AsnWriter.Shared.projitems} (66%) create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/PlatformTraits/LoongArch64GCInfoTraits.cs create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/PlatformTraits/RISCV64GCInfoTraits.cs create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/EEILException.cs create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ExceptionClause.cs create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ExceptionLookupTableEntry.cs create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/InterfaceEntry.cs create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ReadyToRunCoreHeader.cs create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ReadyToRunCoreInfo.cs create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ReadyToRunSection.cs create mode 100644 src/native/managed/cdac/tests/DumpTests/Debuggees/ExceptionHandlingInfo/ExceptionHandlingInfo.csproj create mode 100644 src/native/managed/cdac/tests/DumpTests/Debuggees/ExceptionHandlingInfo/Program.cs create mode 100644 src/native/managed/cdac/tests/DumpTests/Debuggees/RCWInterfaces/Program.cs create mode 100644 src/native/managed/cdac/tests/DumpTests/Debuggees/RCWInterfaces/RCWInterfaces.csproj create mode 100644 src/native/managed/cdac/tests/DumpTests/ExceptionHandlingInfoDumpTests.cs create mode 100644 src/native/managed/cdac/tests/DumpTests/IXCLRDataAppDomainDumpTests.cs create mode 100644 src/native/managed/cdac/tests/DumpTests/IXCLRDataFrameDumpTests.cs create mode 100644 src/native/managed/cdac/tests/DumpTests/RCWInterfacesDumpTests.cs create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_125301/Runtime_125301.cs create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_125327/Runtime_125327.cs create mode 100644 src/tests/JIT/opt/InstructionCombining/Casts.cs create mode 100644 src/tests/JIT/opt/InstructionCombining/Casts.csproj create mode 100644 src/tests/async/devirtualize/devirtualize.cs create mode 100644 src/tests/async/devirtualize/devirtualize.csproj delete mode 100644 src/tests/nativeaot/Directory.Build.targets create mode 100644 src/tests/nativeaot/MobileSmokeTest/MobileSmokeTest.cs create mode 100644 src/tests/nativeaot/MobileSmokeTest/MobileSmokeTest.csproj create mode 100644 src/tests/nativeaot/nativeaot.csproj create mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/TestFramework/VerifyKeptAttributeAttributeWorks.cs create mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/TestFramework/VerifyLocalsAreChanged.cs create mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/TestFramework/VerifyLocalsAreChanged.xml diff --git a/.editorconfig b/.editorconfig index b6cb8e55c913a9..8eb34305d9a6df 100644 --- a/.editorconfig +++ b/.editorconfig @@ -165,6 +165,10 @@ csharp_space_between_square_brackets = false # License header file_header_template = Licensed to the .NET Foundation under one or more agreements.\nThe .NET Foundation licenses this file to you under the MIT license. +# xUnit1051: recommends TestContext.Current.CancellationToken (v3 pattern) which +# is not yet adopted; suppress until a full v3 migration is performed. +dotnet_diagnostic.xUnit1051.severity = none + [src/libraries/System.Net.Http/src/System/Net/Http/{SocketsHttpHandler/Http3RequestStream.cs,BrowserHttpHandler/BrowserHttpHandler.cs}] # disable CA2025, the analyzer throws a NullReferenceException when processing this file: https://github.com/dotnet/roslyn-analyzers/issues/7652 dotnet_diagnostic.CA2025.severity = none diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index f405371e14de23..d3c1221e713383 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -8,7 +8,9 @@ You MUST make your best effort to ensure any code changes satisfy those criteria If you make code changes, do not complete without checking the relevant code builds and relevant tests still pass after the last edits you make. Do not simply assume that your changes fix test failures you see, actually build and run those tests again to confirm. -Before completing, use the `code-review` skill to review your code changes. Any issues flagged as errors or warnings should be addressed before completing. +When running under CCA and before completing, use the `code-review` skill to review your code changes. Any issues flagged as errors or warnings should be addressed before the task is considered complete. + +When NOT running under CCA, skip the `code-review` skill if the user has stated they will review the changes themselves. Before making changes to a directory, search for `README.md` files in that directory and its parent directories up to the repository root. Read any you find — they contain conventions, patterns, and architectural context relevant to your work. @@ -38,6 +40,13 @@ In addition to the rules enforced by `.editorconfig`, you SHOULD: - For markdown (`.md`) files, ensure there is no trailing whitespace at the end of any line. - When adding XML documentation to APIs, follow the guidelines at [`docs.prompt.md`](/.github/prompts/docs.prompt.md). +When NOT running under CCA, guidance for creating commits and pushing changes: + +- Never squash and force push unless explicitly instructed. Always push incremental commits on top of previous PR changes. +- Never push to an active PR without being explicitly asked, even in autopilot/yolo mode. Always wait for explicit instruction to push. +- Never chain commit and push in the same command. Always commit first, report what was committed, then wait for an explicit push instruction. This creates a mandatory decision point. +- Prefer creating a new commit rather than amending an existing one. Exceptions: (1) explicitly asked to amend, or (2) the existing commit is obviously broken with something minor (e.g., typo or comment fix) and hasn't been pushed yet. + --- # Building & Testing in dotnet/runtime diff --git a/THIRD-PARTY-NOTICES.TXT b/THIRD-PARTY-NOTICES.TXT index 033db7e5e46e5d..7f020cf8e67da5 100644 --- a/THIRD-PARTY-NOTICES.TXT +++ b/THIRD-PARTY-NOTICES.TXT @@ -1424,3 +1424,36 @@ NIST-developed software is expressly provided "AS IS." NIST MAKES NO WARRANTY OF You are solely responsible for determining the appropriateness of using and distributing the software and you assume all risks associated with its use, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and the unavailability or interruption of operation. This software is not intended to be used in any situation where a failure could cause risk of injury or damage to property. The software developed by NIST employees is not subject to copyright protection within the United States. +License notice for ANTLR 4 +------------------------------- + +https://github.com/antlr/antlr4 + +Copyright (c) 2012-2022 The ANTLR Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither name of copyright holders nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/docs/design/coreclr/botr/clr-abi.md b/docs/design/coreclr/botr/clr-abi.md index 889a4c9cde656b..3f1cf8dd3fa090 100644 --- a/docs/design/coreclr/botr/clr-abi.md +++ b/docs/design/coreclr/botr/clr-abi.md @@ -116,6 +116,7 @@ To return `Continuation` we use a volatile/calee-trash register that cannot be u | arm | r2 | | arm64 | x2 | | risc-v | a2 | +| loongarch64 | a2 | ### Passing `Continuation` argument The `Continuation` parameter is passed at the same position as generic instantiation parameter or immediately after, if both present. For x86 the argument order is reversed. diff --git a/docs/design/datacontracts/BuiltInCOM.md b/docs/design/datacontracts/BuiltInCOM.md index 4e7cae7200443b..c9e28915501d52 100644 --- a/docs/design/datacontracts/BuiltInCOM.md +++ b/docs/design/datacontracts/BuiltInCOM.md @@ -5,6 +5,20 @@ This contract is for getting information related to built-in COM. ## APIs of contract ``` csharp +public struct COMInterfacePointerData +{ + // Address of the slot in ComCallWrapper that holds the COM interface pointer. + public TargetPointer InterfacePointerAddress; + // MethodTable for this interface, or TargetPointer.Null for slot 0 (IUnknown/IDispatch). + public TargetPointer MethodTable; +} + +public record struct RCWCleanupInfo( + TargetPointer RCW, + TargetPointer Context, + TargetPointer STAThread, + bool IsFreeThreaded); + public ulong GetRefCount(TargetPointer ccw); // Check whether the COM wrappers handle is weak. public bool IsHandleWeak(TargetPointer ccw); @@ -14,20 +28,13 @@ public TargetPointer GetCCWFromInterfacePointer(TargetPointer interfacePointer); // Enumerate the COM interfaces exposed by the ComCallWrapper chain. // ccw may be any ComCallWrapper in the chain; the implementation navigates to the start. public IEnumerable GetCCWInterfaces(TargetPointer ccw); -``` - -where `COMInterfacePointerData` is: -``` csharp -public struct COMInterfacePointerData -{ - // Address of the slot in ComCallWrapper that holds the COM interface pointer. - public TargetPointer InterfacePointerAddress; - // MethodTable for this interface, or TargetPointer.Null for slot 0 (IUnknown/IDispatch). - public TargetPointer MethodTable; -} // Enumerate entries in the RCW cleanup list. // If cleanupListPtr is Null, the global g_pRCWCleanupList is used. public IEnumerable GetRCWCleanupList(TargetPointer cleanupListPtr); +// Enumerate the interface entries cached in an RCW. +public IEnumerable<(TargetPointer MethodTable, TargetPointer Unknown)> GetRCWInterfaces(TargetPointer rcw); +// Get the COM context cookie for an RCW. +public TargetPointer GetRCWContext(TargetPointer rcw); ``` ## Version 1 @@ -51,6 +58,9 @@ Data descriptors used: | `RCW` | `CtxCookie` | COM context cookie for the RCW | | `RCW` | `CtxEntry` | Pointer to `CtxEntry` (bit 0 is a synchronization flag; must be masked off before use) | | `CtxEntry` | `STAThread` | STA thread pointer for the context entry | +| `RCW` | `InterfaceEntries` | Offset of the inline interface entry cache array within the RCW struct | +| `InterfaceEntry` | `MethodTable` | MethodTable pointer for the cached COM interface | +| `InterfaceEntry` | `Unknown` | `IUnknown*` pointer for the cached COM interface | Global variables used: | Global Name | Type | Purpose | @@ -62,6 +72,7 @@ Global variables used: | `TearOffAddRefSimple` | pointer | Address of `Unknown_AddRefSpecial`; identifies `SimpleComCallWrapper` interface pointers | | `TearOffAddRefSimpleInner` | pointer | Address of `Unknown_AddRefInner`; identifies inner `SimpleComCallWrapper` interface pointers | | `RCWCleanupList` | `pointer` | Pointer to the global `g_pRCWCleanupList` instance | +| `RCWInterfaceCacheSize` | `uint32` | Number of entries in the inline interface entry cache (`INTERFACE_ENTRY_CACHE_SIZE`) | ### Contract Constants: | Name | Type | Purpose | Value | @@ -156,5 +167,28 @@ public IEnumerable GetRCWCleanupList(TargetPointer cleanupListPt bucketPtr = _target.ReadPointer(bucketPtr + /* RCW::NextCleanupBucket offset */); } } + +public IEnumerable<(TargetPointer MethodTable, TargetPointer Unknown)> GetRCWInterfaces(TargetPointer rcw) +{ + // InterfaceEntries is an inline array — the offset gives the address of the first element. + TargetPointer interfaceEntriesAddr = rcw + /* RCW::InterfaceEntries offset */; + uint cacheSize = _target.ReadGlobal("RCWInterfaceCacheSize"); + uint entrySize = /* size of InterfaceEntry */; + + for (uint i = 0; i < cacheSize; i++) + { + TargetPointer entryAddress = interfaceEntriesAddr + i * entrySize; + TargetPointer methodTable = _target.ReadPointer(entryAddress + /* InterfaceEntry::MethodTable offset */); + TargetPointer unknown = _target.ReadPointer(entryAddress + /* InterfaceEntry::Unknown offset */); + // An entry is free if Unknown == null (matches InterfaceEntry::IsFree()) + if (unknown != TargetPointer.Null) + yield return (methodTable, unknown); + } +} + +public TargetPointer GetRCWContext(TargetPointer rcw) +{ + return _target.ReadPointer(rcw + /* RCW::CtxCookie offset */); +} ``` diff --git a/docs/design/datacontracts/ExecutionManager.md b/docs/design/datacontracts/ExecutionManager.md index ffe0284b7decb7..12869acd36f828 100644 --- a/docs/design/datacontracts/ExecutionManager.md +++ b/docs/design/datacontracts/ExecutionManager.md @@ -46,10 +46,37 @@ struct CodeBlockHandle // Gets information about the EEJitManager: its address, code type, and head of the code heap list. JitManagerInfo GetEEJitManagerInfo(); + // Get the exception clause info for the code block + List GetExceptionClauses(CodeBlockHandle codeInfoHandle); + // Extension Methods (implemented in terms of other APIs) bool IsFunclet(CodeBlockHandle codeInfoHandle); ``` +```csharp +public struct ExceptionClauseInfo +{ + public enum ExceptionClauseFlags : uint + { + Unknown = 0, + Fault = 0x1, + Finally = 0x2, + Filter = 0x3, + Typed = 0x4 + } + public ExceptionClauseFlags ClauseType; + public bool? IsCatchAllHandler; + public uint TryStartPC; + public uint TryEndPC; + public uint HandlerStartPC; + public uint HandlerEndPC; + public uint? FilterOffset; + public uint? ClassToken; + public TargetNUInt? TypeHandle; + public TargetPointer? ModuleAddr; +} +``` + ## Version 1 The execution manager uses two data structures to map the entire target address space to native executable code. @@ -84,6 +111,7 @@ Data descriptors used: | `RealCodeHeader` | `UnwindInfos` | Start address of Unwind Infos | | `RealCodeHeader` | `DebugInfo` | Pointer to the DebugInfo | | `RealCodeHeader` | `GCInfo` | Pointer to the GCInfo encoding | +| `RealCodeHeader` | `JitEHInfo` | Pointer to the `EE_ILEXCEPTION` containing exception clauses | | `Module` | `ReadyToRunInfo` | Pointer to the `ReadyToRunInfo` for the module | | `ReadyToRunInfo` | `ReadyToRunHeader` | Pointer to the ReadyToRunHeader | | `ReadyToRunInfo` | `CompositeInfo` | Pointer to composite R2R info - or itself for non-composite | @@ -92,8 +120,10 @@ Data descriptors used: | `ReadyToRunInfo` | `NumHotColdMap` | Number of entries in the `HotColdMap` | | `ReadyToRunInfo` | `HotColdMap` | Pointer to an array of 32-bit integers - [see R2R format](../coreclr/botr/readytorun-format.md#readytorunsectiontypehotcoldmap-v80) | | `ReadyToRunInfo` | `DelayLoadMethodCallThunks` | Pointer to an `ImageDataDirectory` for the delay load method call thunks | -| `ReadyToRunInf` | `DebugInfo` | Pointer to an `ImageDataDirectory` for the debug info | +| `ReadyToRunInfo` | `DebugInfo` | Pointer to an `ImageDataDirectory` for the debug info | | `ReadyToRunInfo` | `EntryPointToMethodDescMap` | `HashMap` of entry point addresses to `MethodDesc` pointers | +| `ReadyToRunInfo` | `LoadedImageBase` | Base address of the loaded R2R image | +| `ReadyToRunInfo` | `Composite` | Pointer to the `ReadyToRunCoreInfo` used for section lookup | | `ReadyToRunHeader` | `MajorVersion` | ReadyToRun major version | | `ReadyToRunHeader` | `MinorVersion` | ReadyToRun minor version | | `ImageDataDirectory` | `VirtualAddress` | Virtual address of the image data directory | @@ -106,6 +136,26 @@ Data descriptors used: | `Bucket` | `Values` | Array of values of `HashMapSlotsPerBucket` length | | `UnwindInfo` | `FunctionLength` | Length of the associated function in bytes. Only exists on some platforms | | `PortableEntryPoint` | `MethodDesc` | Method desc of portable entrypoint (only defined if `FeaturePortableEntrypoints` is enabled) | +| `EEILException` | `Clauses` | Start address of the inline array of `EE_ILEXCEPTION_CLAUSE` entries | +| `EEExceptionClause` | `Flags` | Exception clause flags (`COR_ILEXCEPTION_CLAUSE_*` bit flags) | +| `EEExceptionClause` | `TryStartPC` | Native offset of the start of the try block | +| `EEExceptionClause` | `TryEndPC` | Native offset of the end of the try block | +| `EEExceptionClause` | `HandlerStartPC` | Native offset of the start of the handler | +| `EEExceptionClause` | `HandlerEndPC` | Native offset of the end of the handler | +| `EEExceptionClause` | `TypeHandle` | Union field: TypeHandle (cached), ClassToken, or FilterOffset | +| `R2RExceptionClause` | `Flags` | Exception clause flags | +| `R2RExceptionClause` | `TryStartPC` | Native offset of the start of the try block | +| `R2RExceptionClause` | `TryEndPC` | Native offset of the end of the try block | +| `R2RExceptionClause` | `HandlerStartPC` | Native offset of the start of the handler | +| `R2RExceptionClause` | `HandlerEndPC` | Native offset of the end of the handler | +| `R2RExceptionClause` | `ClassToken` | Union field: ClassToken or FilterOffset | +| `ReadyToRunCoreInfo` | `Header` | Pointer to the `READYTORUN_CORE_HEADER` | +| `ReadyToRunCoreHeader` | `Flags` | ReadyToRun flags | +| `ReadyToRunCoreHeader` | `NumberOfSections` | Number of sections following the header | +| `ReadyToRunSection` | `Type` | Section type (`ReadyToRunSectionType`) | +| `ReadyToRunSection` | `Section` | `IMAGE_DATA_DIRECTORY` for the section data | +| `ExceptionLookupTableEntry` | `MethodStartRVA` | RVA of the method start | +| `ExceptionLookupTableEntry` | `ExceptionInfoRVA` | RVA of the exception clause data | Global variables used: | Global Name | Type | Purpose | @@ -119,14 +169,22 @@ Global variables used: | `GCInfoVersion` | uint32 | JITted code GCInfo version | | `FeatureOnStackReplacement` | uint8 | 1 if FEATURE_ON_STACK_REPLACEMENT is enabled, 0 otherwise | | `FeaturePortableEntrypoints` | uint8 | 1 if FEATURE_PORTABLE_ENTRYPOINTS is enabled, 0 otherwise | +| `ObjectMethodTable` | TargetPointer | Pointer to the `System.Object` MethodTable, used for catch-all handler detection | + +Contract constants used: +| Name | Type | Purpose | Value | +| --- | --- | --- | --- | +| `CachedClass` | `uint` | Bit flag to indicate exception clause contains a cached TypeHandle | `0x10000000` | Contracts used: | Contract Name | | --- | | `PlatformMetadata` | | `GCInfo` | +| `Loader` | | `PrecodeStubs` | | `RuntimeInfo` | +| `RuntimeTypeSystem` | The bulk of the work is done by the `GetCodeBlockHandle` API that maps a code pointer to information about the containing jitted method. This relies the [range section lookup](#rangesectionmap). @@ -382,6 +440,16 @@ For R2R images, `hasFlagByte` is always `false`. `IsFunclet` is implemented in terms of `IExecutionManager.GetStartAddress` and `IExecutionManager.GetFuncletStartAddress`. If the values are the same, the code block handle is not a funclet. If they are different, it is a funclet. +`IExecutionManager.GetExceptionClauses` enumerates the exception handling clauses for a given code block. The ExecutionManager delegates to the JitManager implementations to obtain the start and end addresses of the clause array, since JIT-compiled and ReadyToRun code store exception clauses in different formats and locations. + +There are two distinct clause data types. JIT-compiled code uses `EEExceptionClause` (corresponding to `EE_ILEXCEPTION_CLAUSE`), which has a pointer-sized union field that can hold a `TypeHandle`, `ClassToken`, or `FilterOffset`. ReadyToRun code uses `R2RExceptionClause` (corresponding to `CORCOMPILE_EXCEPTION_CLAUSE`), which has a 4-byte union field containing only `ClassToken` or `FilterOffset`. Both types share the same common fields: `Flags`, `TryStartPC`, `TryEndPC`, `HandlerStartPC`, and `HandlerEndPC`. + +* For jitted code (`EEJitManager`), the exception clauses are stored in an `EE_ILEXCEPTION` structure pointed to by the `JitEHInfo` field of the `RealCodeHeader`. The `EEILException` data type wraps this structure: its `Clauses` field gives the address of the first clause (at `offsetof(EE_ILEXCEPTION, Clauses)`, skipping the 4-byte `COR_ILMETHOD_SECT_FAT` header). The number of clauses is stored as a pointer-sized integer immediately before the `EE_ILEXCEPTION` structure (at `JitEHInfo.Address - sizeof(pointer)`). The clause array is strided using the size of `EEExceptionClause`. + +* For R2R code (`ReadyToRunJitManager`), exception clause data is found via the `ExceptionInfo` section (section type 104) of the R2R image. The section is located by traversing `ReadyToRunInfo::Composite` to reach the `ReadyToRunCoreInfo`, then reading its `Header` pointer to the `ReadyToRunCoreHeader`, and iterating through the inline `ReadyToRunSection` array that immediately follows the header. The `ExceptionInfo` section contains an `ExceptionLookupTableEntry` array, where each entry maps a `MethodStartRVA` to an `ExceptionInfoRVA`. A binary search (falling back to linear scan for small ranges) finds the entry matching the method's RVA. The exception clauses span from that entry's `ExceptionInfoRVA` to the next entry's `ExceptionInfoRVA`, both offset from the image base. The clause array is strided using the size of `R2RExceptionClause`. + +After obtaining the clause array bounds, the common iteration logic classifies each clause by its flags. The native `COR_ILEXCEPTION_CLAUSE` flags are bit flags: `Filter` (0x1), `Finally` (0x2), `Fault` (0x4). If none are set, the clause is `Typed`. For typed clauses, if the `CachedClass` flag (0x10000000) is set (JIT-only, used for dynamic methods), the union field contains a resolved `TypeHandle` pointer; the clause is a catch-all if this pointer equals the `ObjectMethodTable` global. Otherwise, the union field is a metadata `ClassToken`. To determine whether a typed clause is a catch-all handler, the `ClassToken` (which may be a `TypeDef` or `TypeRef`) is resolved to a `MethodTable` via the `Loader` contract's module lookup maps (`TypeDefToMethodTable` or `TypeRefToMethodTable`) and compared against the `ObjectMethodTable` global. For typed clauses without a cached type handle, the module address is resolved by walking `CodeBlockHandle` → `MethodDesc` → `MethodTable` → `TypeHandle` → `Module` via the `RuntimeTypeSystem` contract. + ### RangeSectionMap The range section map logically partitions the entire 32-bit or 64-bit addressable space into chunks. diff --git a/docs/design/datacontracts/Loader.md b/docs/design/datacontracts/Loader.md index 0be000bb750063..95f1652cf1b805 100644 --- a/docs/design/datacontracts/Loader.md +++ b/docs/design/datacontracts/Loader.md @@ -182,6 +182,7 @@ IReadOnlyDictionary GetLoaderAllocatorHeaps(TargetPointer | Name | Type | Purpose | Value | | --- | --- | --- | --- | | `ASSEMBLY_NOTIFYFLAGS_PROFILER_NOTIFIED` | uint | Flag in Assembly NotifyFlags indicating the Assembly will notify profilers. | `0x1` | +| `DefaultDomainFriendlyName` | string | Friendly name returned when `AppDomain.FriendlyName` is null (matches native `DEFAULT_DOMAIN_FRIENDLY_NAME`) | `"DefaultDomain"` | Contracts used: | Contract Name | @@ -327,8 +328,11 @@ string ILoader.GetAppDomainFriendlyName() { TargetPointer appDomainPointer = target.ReadGlobalPointer("AppDomain"); TargetPointer appDomain = target.ReadPointer(appDomainPointer) - TargetPointer pathStart = appDomain + /* AppDomain::FriendlyName offset */; - char[] name = // Read from target starting at pathStart until null terminator + TargetPointer namePtr = appDomain + /* AppDomain::FriendlyName offset */; + // Match native AppDomain::GetFriendlyName(): return "DefaultDomain" when pointer is null. + if (namePtr == TargetPointer.Null) + return "DefaultDomain"; + char[] name = // Read from target starting at namePtr until null terminator return new string(name); } diff --git a/eng/Version.Details.props b/eng/Version.Details.props index f69559c4dbf965..295cb18938b7a2 100644 --- a/eng/Version.Details.props +++ b/eng/Version.Details.props @@ -6,45 +6,45 @@ This file should be imported by eng/Versions.props - 5.6.0-2.26160.112 - 5.6.0-2.26160.112 - 5.6.0-2.26160.112 - 11.0.100-preview.3.26160.112 - 11.0.100-preview.3.26160.112 - 11.0.0-beta.26160.112 - 11.0.0-beta.26160.112 - 11.0.0-beta.26160.112 - 11.0.0-beta.26160.112 - 11.0.0-beta.26160.112 - 11.0.0-beta.26160.112 - 11.0.0-beta.26160.112 - 11.0.0-beta.26160.112 - 0.11.5-preview.26160.112 - 11.0.0-beta.26160.112 - 11.0.0-beta.26160.112 - 11.0.0-beta.26160.112 - 11.0.0-beta.26160.112 - 11.0.0-beta.26160.112 - 11.0.0-beta.26160.112 - 11.0.0-beta.26160.112 - 11.0.0-beta.26160.112 - 2.9.3-beta.26160.112 - 2.9.3-beta.26160.112 - 11.0.0-beta.26160.112 - 5.6.0-2.26160.112 - 11.0.0-preview.3.26160.112 - 11.0.100-preview.3.26160.112 - 11.0.0-preview.3.26160.112 - 11.0.0-preview.3.26160.112 - 7.5.0-rc.16112 - 7.5.0-rc.16112 - 7.5.0-rc.16112 - 7.5.0-rc.16112 - 11.0.0-preview.3.26160.112 - 3.0.0-preview.3.26160.112 - 11.0.0-preview.3.26160.112 - 11.0.0-preview.3.26160.112 - 11.0.0-preview.3.26160.112 + 5.6.0-2.26163.109 + 5.6.0-2.26163.109 + 5.6.0-2.26163.109 + 11.0.100-preview.3.26163.109 + 11.0.100-preview.3.26163.109 + 11.0.0-beta.26163.109 + 11.0.0-beta.26163.109 + 11.0.0-beta.26163.109 + 11.0.0-beta.26163.109 + 11.0.0-beta.26163.109 + 11.0.0-beta.26163.109 + 11.0.0-beta.26163.109 + 11.0.0-beta.26163.109 + 0.11.5-preview.26163.109 + 11.0.0-beta.26163.109 + 11.0.0-beta.26163.109 + 11.0.0-beta.26163.109 + 11.0.0-beta.26163.109 + 11.0.0-beta.26163.109 + 11.0.0-beta.26163.109 + 11.0.0-beta.26163.109 + 11.0.0-beta.26163.109 + 2.9.3-beta.26163.109 + 2.9.3-beta.26163.109 + 11.0.0-beta.26163.109 + 5.6.0-2.26163.109 + 11.0.0-preview.3.26163.109 + 11.0.100-preview.3.26163.109 + 11.0.0-preview.3.26163.109 + 11.0.0-preview.3.26163.109 + 7.6.0-rc.16409 + 7.6.0-rc.16409 + 7.6.0-rc.16409 + 7.6.0-rc.16409 + 11.0.0-preview.3.26163.109 + 3.0.0-preview.3.26163.109 + 11.0.0-preview.3.26163.109 + 11.0.0-preview.3.26163.109 + 11.0.0-preview.3.26163.109 11.0.0-alpha.0.25625.1 @@ -113,9 +113,9 @@ This file should be imported by eng/Versions.props 11.0.0-beta.26159.1 11.0.0-beta.26159.1 - 11.0.0-prerelease.26064.3 - 11.0.0-prerelease.26064.3 - 11.0.0-prerelease.26064.3 + 11.0.0-prerelease.26160.2 + 11.0.0-prerelease.26160.2 + 11.0.0-prerelease.26160.2 diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index ac48af7b078099..ad8c1a8e9ca029 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,5 +1,5 @@ - + https://github.com/dotnet/icu @@ -37,91 +37,91 @@ https://github.com/dotnet/llvm-project 07cf3afbd65f821dc9aec958aab04fa37fdce2ec - + https://github.com/dotnet/dotnet - 542fea6988c173c64859bab06c9d2d100034843c + 08e21da2d882e5ae91f5fe0adf11568cb00c2d1e - + https://github.com/dotnet/dotnet - 542fea6988c173c64859bab06c9d2d100034843c + 08e21da2d882e5ae91f5fe0adf11568cb00c2d1e - + https://github.com/dotnet/dotnet - 542fea6988c173c64859bab06c9d2d100034843c + 08e21da2d882e5ae91f5fe0adf11568cb00c2d1e - + https://github.com/dotnet/dotnet - 542fea6988c173c64859bab06c9d2d100034843c + 08e21da2d882e5ae91f5fe0adf11568cb00c2d1e - + https://github.com/dotnet/dotnet - 542fea6988c173c64859bab06c9d2d100034843c + 08e21da2d882e5ae91f5fe0adf11568cb00c2d1e - + https://github.com/dotnet/dotnet - 542fea6988c173c64859bab06c9d2d100034843c + 08e21da2d882e5ae91f5fe0adf11568cb00c2d1e - + https://github.com/dotnet/dotnet - 542fea6988c173c64859bab06c9d2d100034843c + 08e21da2d882e5ae91f5fe0adf11568cb00c2d1e - + https://github.com/dotnet/dotnet - 542fea6988c173c64859bab06c9d2d100034843c + 08e21da2d882e5ae91f5fe0adf11568cb00c2d1e - + https://github.com/dotnet/dotnet - 542fea6988c173c64859bab06c9d2d100034843c + 08e21da2d882e5ae91f5fe0adf11568cb00c2d1e - + https://github.com/dotnet/dotnet - 542fea6988c173c64859bab06c9d2d100034843c + 08e21da2d882e5ae91f5fe0adf11568cb00c2d1e - + https://github.com/dotnet/dotnet - 542fea6988c173c64859bab06c9d2d100034843c + 08e21da2d882e5ae91f5fe0adf11568cb00c2d1e - + https://github.com/dotnet/dotnet - 542fea6988c173c64859bab06c9d2d100034843c + 08e21da2d882e5ae91f5fe0adf11568cb00c2d1e - + https://github.com/dotnet/dotnet - 542fea6988c173c64859bab06c9d2d100034843c + 08e21da2d882e5ae91f5fe0adf11568cb00c2d1e - + https://github.com/dotnet/dotnet - 542fea6988c173c64859bab06c9d2d100034843c + 08e21da2d882e5ae91f5fe0adf11568cb00c2d1e - + https://github.com/dotnet/dotnet - 542fea6988c173c64859bab06c9d2d100034843c + 08e21da2d882e5ae91f5fe0adf11568cb00c2d1e - + https://github.com/dotnet/dotnet - 542fea6988c173c64859bab06c9d2d100034843c + 08e21da2d882e5ae91f5fe0adf11568cb00c2d1e - + https://github.com/dotnet/dotnet - 542fea6988c173c64859bab06c9d2d100034843c + 08e21da2d882e5ae91f5fe0adf11568cb00c2d1e - + https://github.com/dotnet/dotnet - 542fea6988c173c64859bab06c9d2d100034843c + 08e21da2d882e5ae91f5fe0adf11568cb00c2d1e - + https://github.com/dotnet/dotnet - 542fea6988c173c64859bab06c9d2d100034843c + 08e21da2d882e5ae91f5fe0adf11568cb00c2d1e - + https://github.com/dotnet/dotnet - 542fea6988c173c64859bab06c9d2d100034843c + 08e21da2d882e5ae91f5fe0adf11568cb00c2d1e - + https://github.com/dotnet/dotnet - 542fea6988c173c64859bab06c9d2d100034843c + 08e21da2d882e5ae91f5fe0adf11568cb00c2d1e https://github.com/dotnet/runtime-assets @@ -259,49 +259,49 @@ https://github.com/dotnet/llvm-project 07cf3afbd65f821dc9aec958aab04fa37fdce2ec - + https://github.com/dotnet/dotnet - 542fea6988c173c64859bab06c9d2d100034843c + 08e21da2d882e5ae91f5fe0adf11568cb00c2d1e - + https://github.com/dotnet/dotnet - 542fea6988c173c64859bab06c9d2d100034843c + 08e21da2d882e5ae91f5fe0adf11568cb00c2d1e - + https://github.com/dotnet/dotnet - 542fea6988c173c64859bab06c9d2d100034843c + 08e21da2d882e5ae91f5fe0adf11568cb00c2d1e - + https://github.com/dotnet/dotnet - 542fea6988c173c64859bab06c9d2d100034843c + 08e21da2d882e5ae91f5fe0adf11568cb00c2d1e - + https://github.com/dotnet/dotnet - 542fea6988c173c64859bab06c9d2d100034843c + 08e21da2d882e5ae91f5fe0adf11568cb00c2d1e - + https://github.com/dotnet/dotnet - 542fea6988c173c64859bab06c9d2d100034843c + 08e21da2d882e5ae91f5fe0adf11568cb00c2d1e - + https://github.com/dotnet/dotnet - 542fea6988c173c64859bab06c9d2d100034843c + 08e21da2d882e5ae91f5fe0adf11568cb00c2d1e - + https://github.com/dotnet/xharness - 31e0b8e08f57890f7b7004b93361d69cd4b21079 + c32a7777a0f8f7a4fc8d9920d445f5f4b5658d38 - + https://github.com/dotnet/xharness - 31e0b8e08f57890f7b7004b93361d69cd4b21079 + c32a7777a0f8f7a4fc8d9920d445f5f4b5658d38 - + https://github.com/dotnet/xharness - 31e0b8e08f57890f7b7004b93361d69cd4b21079 + c32a7777a0f8f7a4fc8d9920d445f5f4b5658d38 - + https://github.com/dotnet/dotnet - 542fea6988c173c64859bab06c9d2d100034843c + 08e21da2d882e5ae91f5fe0adf11568cb00c2d1e https://dev.azure.com/dnceng/internal/_git/dotnet-optimization @@ -327,29 +327,29 @@ https://github.com/dotnet/runtime-assets 027a725187a44e48327b7f8a0e181b9b9c036ae7 - + https://github.com/dotnet/dotnet - 542fea6988c173c64859bab06c9d2d100034843c + 08e21da2d882e5ae91f5fe0adf11568cb00c2d1e - + https://github.com/dotnet/dotnet - 542fea6988c173c64859bab06c9d2d100034843c + 08e21da2d882e5ae91f5fe0adf11568cb00c2d1e - + https://github.com/dotnet/dotnet - 542fea6988c173c64859bab06c9d2d100034843c + 08e21da2d882e5ae91f5fe0adf11568cb00c2d1e - + https://github.com/dotnet/dotnet - 542fea6988c173c64859bab06c9d2d100034843c + 08e21da2d882e5ae91f5fe0adf11568cb00c2d1e - + https://github.com/dotnet/dotnet - 542fea6988c173c64859bab06c9d2d100034843c + 08e21da2d882e5ae91f5fe0adf11568cb00c2d1e - + https://github.com/dotnet/dotnet - 542fea6988c173c64859bab06c9d2d100034843c + 08e21da2d882e5ae91f5fe0adf11568cb00c2d1e https://dev.azure.com/dnceng/internal/_git/dotnet-optimization @@ -361,21 +361,21 @@ - + https://github.com/dotnet/dotnet - 542fea6988c173c64859bab06c9d2d100034843c + 08e21da2d882e5ae91f5fe0adf11568cb00c2d1e - + https://github.com/dotnet/dotnet - 542fea6988c173c64859bab06c9d2d100034843c + 08e21da2d882e5ae91f5fe0adf11568cb00c2d1e - + https://github.com/dotnet/dotnet - 542fea6988c173c64859bab06c9d2d100034843c + 08e21da2d882e5ae91f5fe0adf11568cb00c2d1e - + https://github.com/dotnet/dotnet - 542fea6988c173c64859bab06c9d2d100034843c + 08e21da2d882e5ae91f5fe0adf11568cb00c2d1e https://github.com/dotnet/node diff --git a/eng/common/core-templates/post-build/post-build.yml b/eng/common/core-templates/post-build/post-build.yml index 9438429ca3781e..88806f1e508eda 100644 --- a/eng/common/core-templates/post-build/post-build.yml +++ b/eng/common/core-templates/post-build/post-build.yml @@ -206,7 +206,7 @@ stages: displayName: Validate inputs: filePath: eng\common\sdk-task.ps1 - arguments: -task SigningValidation -restore -msbuildEngine vs + arguments: -task SigningValidation -restore /p:PackageBasePath='$(Build.ArtifactStagingDirectory)/PackageArtifacts' /p:SignCheckExclusionsFile='$(System.DefaultWorkingDirectory)/eng/SignCheckExclusionsFile.txt' ${{ parameters.signingValidationAdditionalParameters }} diff --git a/eng/native.wasm.targets b/eng/native.wasm.targets index a8966ecdb85e9b..69067b6b0a7d41 100644 --- a/eng/native.wasm.targets +++ b/eng/native.wasm.targets @@ -9,6 +9,7 @@ <_RuntimeVariant /> <_RuntimeVariant Condition="'$(WasmEnableThreads)' == 'true'">-threads + true <_IcuDir Condition="'$(PkgMicrosoft_NETCore_Runtime_ICU_Transport)' != '' and '$(TargetsWasm)' == 'true'">$(PkgMicrosoft_NETCore_Runtime_ICU_Transport)/runtimes/$(TargetOS)-$(TargetArchitecture)$(_RuntimeVariant)/native <_TzdDir Condition="'$(PkgSystem_Runtime_TimeZoneData)' != '' and '$(TargetsWasm)' == 'true'">$([MSBuild]::NormalizePath('$(PkgSystem_Runtime_TimeZoneData)', 'contentFiles', 'any', 'any', 'data')) @@ -33,18 +34,18 @@ - + - - - --> diff --git a/eng/pipelines/extra-platforms/runtime-extra-platforms-ioslike.yml b/eng/pipelines/extra-platforms/runtime-extra-platforms-ioslike.yml index da7fff53452b34..d5a9d77ac0b842 100644 --- a/eng/pipelines/extra-platforms/runtime-extra-platforms-ioslike.yml +++ b/eng/pipelines/extra-platforms/runtime-extra-platforms-ioslike.yml @@ -163,7 +163,7 @@ jobs: - template: /eng/pipelines/common/templates/runtimes/build-runtime-tests-and-send-to-helix.yml parameters: creator: dotnet-bot - testBuildArgs: tree nativeaot/SmokeTests /p:BuildNativeAOTRuntimePack=true + testBuildArgs: tree nativeaot/MobileSmokeTest /p:BuildNativeAOTRuntimePack=true testRunNamePrefixSuffix: NativeAOT_$(_BuildConfig) buildAllTestsAsStandalone: true diff --git a/eng/pipelines/extra-platforms/runtime-extra-platforms-ioslikesimulator.yml b/eng/pipelines/extra-platforms/runtime-extra-platforms-ioslikesimulator.yml index 9a5d8939d1f06a..f5044b5d50630f 100644 --- a/eng/pipelines/extra-platforms/runtime-extra-platforms-ioslikesimulator.yml +++ b/eng/pipelines/extra-platforms/runtime-extra-platforms-ioslikesimulator.yml @@ -130,7 +130,7 @@ jobs: - template: /eng/pipelines/common/templates/runtimes/build-runtime-tests-and-send-to-helix.yml parameters: creator: dotnet-bot - testBuildArgs: tree nativeaot/SmokeTests /p:BuildNativeAOTRuntimePack=true + testBuildArgs: tree nativeaot/MobileSmokeTest /p:BuildNativeAOTRuntimePack=true testRunNamePrefixSuffix: NativeAOT_$(_BuildConfig) buildAllTestsAsStandalone: true diff --git a/eng/pipelines/extra-platforms/runtime-extra-platforms-maccatalyst.yml b/eng/pipelines/extra-platforms/runtime-extra-platforms-maccatalyst.yml index cd2bba5421b151..92cba45048e616 100644 --- a/eng/pipelines/extra-platforms/runtime-extra-platforms-maccatalyst.yml +++ b/eng/pipelines/extra-platforms/runtime-extra-platforms-maccatalyst.yml @@ -162,7 +162,7 @@ jobs: parameters: creator: dotnet-bot buildAllTestsAsStandalone: true - testBuildArgs: tree nativeaot/SmokeTests /p:BuildNativeAOTRuntimePack=true + testBuildArgs: tree nativeaot/MobileSmokeTest /p:BuildNativeAOTRuntimePack=true testRunNamePrefixSuffix: NativeAOT_$(_BuildConfig) # @@ -201,7 +201,7 @@ jobs: parameters: creator: dotnet-bot buildAllTestsAsStandalone: true - testBuildArgs: tree nativeaot/SmokeTests /p:BuildNativeAOTRuntimePack=true /p:DevTeamProvisioning=adhoc /p:EnableAppSandbox=true + testBuildArgs: tree nativeaot/MobileSmokeTest /p:BuildNativeAOTRuntimePack=true /p:DevTeamProvisioning=adhoc /p:EnableAppSandbox=true testRunNamePrefixSuffix: NativeAOT_$(_BuildConfig) # diff --git a/eng/testing/BrowserVersions.props b/eng/testing/BrowserVersions.props index dd5f54e162bf27..0725d73db6ce2b 100644 --- a/eng/testing/BrowserVersions.props +++ b/eng/testing/BrowserVersions.props @@ -8,7 +8,7 @@ 1536371 https://storage.googleapis.com/chromium-browser-snapshots/Mac_Arm/1536376 14.3.127 - 146.0.7680.31 + 146.0.7680.66 1582197 https://storage.googleapis.com/chromium-browser-snapshots/Win_x64/1582218 14.6.202 diff --git a/eng/testing/tests.targets b/eng/testing/tests.targets index 1dd5cdd9dfbdaa..9336d5bdc24913 100644 --- a/eng/testing/tests.targets +++ b/eng/testing/tests.targets @@ -4,7 +4,6 @@ and '$(TestNativeAot)' != 'true' and ('$(TestReadyToRun)' != 'true' or '$(UseRuntimeAsync)' == 'true') and '$(UseNativeAOTRuntime)' != 'true' - and '$(TargetOS)' != 'browser' and '$(TargetOS)' != 'wasi' and '$(TargetOS)' != 'android' and '$(TargetsAppleMobile)' != 'true' diff --git a/eng/testing/xunit/xunit.props b/eng/testing/xunit/xunit.props index 457c4f402bf78b..47148592eedff2 100644 --- a/eng/testing/xunit/xunit.props +++ b/eng/testing/xunit/xunit.props @@ -3,6 +3,9 @@ en $(MSBuildThisFileDirectory)xunit.runner.json + + $(NoWarn);xUnit1051 diff --git a/global.json b/global.json index 6c24d95c0c1c9a..61cc0e68e65335 100644 --- a/global.json +++ b/global.json @@ -1,18 +1,18 @@ { "sdk": { - "version": "11.0.100-preview.3.26128.104", + "version": "11.0.100-preview.3.26161.119", "allowPrerelease": true, "rollForward": "major" }, "tools": { - "dotnet": "11.0.100-preview.3.26128.104" + "dotnet": "11.0.100-preview.3.26161.119" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "11.0.0-beta.26160.112", - "Microsoft.DotNet.Helix.Sdk": "11.0.0-beta.26160.112", - "Microsoft.DotNet.SharedFramework.Sdk": "11.0.0-beta.26160.112", + "Microsoft.DotNet.Arcade.Sdk": "11.0.0-beta.26163.109", + "Microsoft.DotNet.Helix.Sdk": "11.0.0-beta.26163.109", + "Microsoft.DotNet.SharedFramework.Sdk": "11.0.0-beta.26163.109", "Microsoft.Build.NoTargets": "3.7.0", "Microsoft.Build.Traversal": "3.4.0", - "Microsoft.NET.Sdk.IL": "11.0.0-preview.3.26160.112" + "Microsoft.NET.Sdk.IL": "11.0.0-preview.3.26163.109" } } diff --git a/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComActivator.cs b/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComActivator.cs index 5ac78f35cdcee0..33b82d2fbb07e6 100644 --- a/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComActivator.cs +++ b/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComActivator.cs @@ -762,10 +762,17 @@ private static extern bool Contains( [UnsafeAccessorType(LicInfoHelperLicenseContextTypeName)] object? licInfoHelperContext, string assemblyName); - // Helper function to create an object from the native side - public static object Create() + [UnmanagedCallersOnly] + private static unsafe void Create(object* pResult, Exception* pException) { - return new LicenseInteropProxy(); + try + { + *pResult = new LicenseInteropProxy(); + } + catch (Exception ex) + { + *pException = ex; + } } // Determine if the type supports licensing @@ -877,6 +884,19 @@ public void GetCurrentContextInfo(RuntimeTypeHandle rth, out bool isDesignTime, bstrKey = Marshal.StringToBSTR((string)key!); } + [UnmanagedCallersOnly] + private static unsafe void GetCurrentContextInfo(LicenseInteropProxy* pProxy, Type* pType, bool* pIsDesignTime, IntPtr* pBstrKey, Exception* pException) + { + try + { + pProxy->GetCurrentContextInfo(pType->TypeHandle, out *pIsDesignTime, out *pBstrKey); + } + catch (Exception ex) + { + *pException = ex; + } + } + // The CLR invokes this when instantiating a licensed COM // object inside a designtime license context. // It's purpose is to save away the license key that the CLR @@ -892,5 +912,18 @@ public void SaveKeyInCurrentContext(IntPtr bstrKey) SetSavedLicenseKey(_licContext!, _targetRcwType!, key); } + + [UnmanagedCallersOnly] + private static unsafe void SaveKeyInCurrentContext(LicenseInteropProxy* pProxy, IntPtr bstrKey, Exception* pException) + { + try + { + pProxy->SaveKeyInCurrentContext(bstrKey); + } + catch (Exception ex) + { + *pException = ex; + } + } } } diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs index 4ad338f0dc6a6d..3772691fc6a633 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs @@ -4219,6 +4219,39 @@ private object InvokeDispMethod( return ret; } + [RequiresUnreferencedCode("The member might be removed")] + [UnmanagedCallersOnly] + private static unsafe void ForwardCallToInvokeMember( + RuntimeType* pRuntimeType, + string* pMemberName, + int flags, + object* pTarget, + object[]* pArgs, + bool[]* pArgsIsByRef, + int[]* pArgsWrapperTypes, + Type[]* pArgsTypes, + Type* pRetType, + object* pResult, + Exception* pException) + { + try + { + *pResult = pRuntimeType->ForwardCallToInvokeMember( + *pMemberName, + (BindingFlags)flags, + *pTarget, + *pArgs, + *pArgsIsByRef, + *pArgsWrapperTypes, + *pArgsTypes, + *pRetType); + } + catch (Exception ex) + { + *pException = ex; + } + } + private static void WrapArgsForInvokeCall(object[] aArgs, int[] aArgsWrapperTypes) { int cArgs = aArgs.Length; diff --git a/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs b/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs index b7b1601dfa5f1c..a913fca431da0a 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs @@ -1491,6 +1491,20 @@ internal static void SetPendingExceptionObject(Exception? exception) #if FEATURE_COMINTEROP [SupportedOSPlatform("windows")] internal static object GetIEnumeratorToEnumVariantMarshaler() => EnumeratorToEnumVariantMarshaler.GetInstance(string.Empty); + + [SupportedOSPlatform("windows")] + [UnmanagedCallersOnly] + private static unsafe void GetIEnumeratorToEnumVariantMarshaler(object* pResult, Exception* pException) + { + try + { + *pResult = GetIEnumeratorToEnumVariantMarshaler(); + } + catch (Exception ex) + { + *pException = ex; + } + } #endif internal static object CreateCustomMarshaler(IntPtr pMD, int paramToken, IntPtr hndManagedType) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Variant.cs b/src/coreclr/System.Private.CoreLib/src/System/Variant.cs index 2296297be09108..0d2cb9c302c259 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Variant.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Variant.cs @@ -192,6 +192,19 @@ internal static void MarshalHelperConvertObjectToVariant(object? o, out ComVaria } } + [UnmanagedCallersOnly] + private static unsafe void MarshalHelperConvertObjectToVariant(object* pObject, ComVariant* pOle, Exception* pException) + { + try + { + MarshalHelperConvertObjectToVariant(*pObject, out *pOle); + } + catch (Exception ex) + { + *pException = ex; + } + } + // Helper code for marshaling VARIANTS to managed objects internal static unsafe object? MarshalHelperConvertVariantToObject(ref readonly ComVariant pOle) { @@ -320,6 +333,19 @@ internal static void MarshalHelperConvertObjectToVariant(object? o, out ComVaria } } + [UnmanagedCallersOnly] + private static unsafe void MarshalHelperConvertVariantToObject(ComVariant* pOle, object* pResult, Exception* pException) + { + try + { + *pResult = MarshalHelperConvertVariantToObject(in *pOle); + } + catch (Exception ex) + { + *pException = ex; + } + } + // Helper code: on the back propagation path where a VT_BYREF VARIANT* // is marshaled to a "ref Object", we use this helper to force the // updated object back to the original type. @@ -396,5 +422,18 @@ internal static void MarshalHelperCastVariant(object pValue, int vt, out ComVari }; } } + + [UnmanagedCallersOnly] + private static unsafe void MarshalHelperCastVariant(object* pValue, int vt, ComVariant* pOle, Exception* pException) + { + try + { + MarshalHelperCastVariant(*pValue!, vt, out *pOle); + } + catch (Exception ex) + { + *pException = ex; + } + } } } diff --git a/src/coreclr/System.Private.CoreLib/src/System/__ComObject.cs b/src/coreclr/System.Private.CoreLib/src/System/__ComObject.cs index a520097018d9f4..5ef7080d53b1bf 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/__ComObject.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/__ComObject.cs @@ -126,6 +126,20 @@ internal object GetEventProvider( return CreateEventProvider(t); } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2062:Value passed to parameter cannot be statically determined", Justification = "The runtime passes a RuntimeType describing the COM event provider. The dynamic constructor access requirements are enforced by runtime callsite semantics.")] + [UnmanagedCallersOnly] + private static unsafe void GetEventProvider(__ComObject* pComObject, RuntimeType* pProviderType, object* pResult, Exception* pException) + { + try + { + *pResult = pComObject->GetEventProvider(*pProviderType); + } + catch (Exception ex) + { + *pException = ex; + } + } + private object CreateEventProvider( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] RuntimeType t) { diff --git a/src/coreclr/clr.featuredefines.props b/src/coreclr/clr.featuredefines.props index 23e5e360b0ae32..76677d3c6148c2 100644 --- a/src/coreclr/clr.featuredefines.props +++ b/src/coreclr/clr.featuredefines.props @@ -7,6 +7,7 @@ true true true + true @@ -17,10 +18,8 @@ true true true - true - true false false @@ -66,9 +65,6 @@ $(DefineConstants);FEATURE_DYNAMIC_CODE_COMPILED $(DefineConstants);FEATURE_PORTABLE_ENTRYPOINTS $(DefineConstants);FEATURE_PORTABLE_HELPERS - $(DefineConstants);FEATURE_WASM_MANAGED_THREADS - $(DefineConstants);FEATURE_SINGLE_THREADED - $(DefineConstants);PROFILING_SUPPORTED diff --git a/src/coreclr/clrdefinitions.cmake b/src/coreclr/clrdefinitions.cmake index 94816f7309b642..56e4e9cd75d176 100644 --- a/src/coreclr/clrdefinitions.cmake +++ b/src/coreclr/clrdefinitions.cmake @@ -100,9 +100,9 @@ endif(FEATURE_EVENTSOURCE_XPLAT) if(NOT CLR_CMAKE_TARGET_NETBSD AND NOT CLR_CMAKE_TARGET_ARCH_WASM) add_definitions(-DFEATURE_HIJACK) endif(NOT CLR_CMAKE_TARGET_NETBSD AND NOT CLR_CMAKE_TARGET_ARCH_WASM) -if(FEATURE_SINGLE_THREADED) - add_definitions(-DFEATURE_SINGLE_THREADED) -endif(FEATURE_SINGLE_THREADED) +if(NOT CLR_CMAKE_TARGET_ARCH_WASM OR FEATURE_MULTITHREADING) + add_definitions(-DFEATURE_MULTITHREADING) +endif(NOT CLR_CMAKE_TARGET_ARCH_WASM OR FEATURE_MULTITHREADING) if (CLR_CMAKE_TARGET_WIN32 AND (CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_I386 OR CLR_CMAKE_TARGET_ARCH_ARM64)) add_definitions(-DFEATURE_INTEROP_DEBUGGING) endif (CLR_CMAKE_TARGET_WIN32 AND (CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_I386 OR CLR_CMAKE_TARGET_ARCH_ARM64)) diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index 015168aa6494fa..9e368c4d99ded0 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -5629,6 +5629,16 @@ HRESULT DacDbiInterfaceImpl::Hijack(VMPTR_Thread vmThread, ULONG32 dwThreadId, c ctx.X1 = (DWORD64)espRecord; ctx.X2 = (DWORD64)reason; ctx.X3 = (DWORD64)pData; + #elif defined(TARGET_RISCV64) + ctx.A0 = (DWORD64)espContext; + ctx.A1 = (DWORD64)espRecord; + ctx.A2 = (DWORD64)reason; + ctx.A3 = (DWORD64)pData; + #elif defined(TARGET_LOONGARCH64) + ctx.A0 = (DWORD64)espContext; + ctx.A1 = (DWORD64)espRecord; + ctx.A2 = (DWORD64)reason; + ctx.A3 = (DWORD64)pData; #else PORTABILITY_ASSERT("CordbThread::HijackForUnhandledException is not implemented on this platform."); #endif diff --git a/src/coreclr/debug/daccess/request.cpp b/src/coreclr/debug/daccess/request.cpp index c4af77adc6bfa0..b644768a3e90c1 100644 --- a/src/coreclr/debug/daccess/request.cpp +++ b/src/coreclr/debug/daccess/request.cpp @@ -3454,24 +3454,25 @@ ClrDataAccess::TraverseEHInfo(CLRDATA_ADDRESS ip, DUMPEHINFO pFunc, LPVOID token else if (IsTypedHandler(&EHClause)) { deh.clauseType = EHTyped; - deh.isCatchAllHandler = (&EHClause.TypeHandle == (void*)(size_t)mdTypeRefNil); + if (HasCachedTypeHandle(&EHClause)) + { + deh.mtCatch = TO_CDADDR(EHClause.TypeHandle); + deh.isCatchAllHandler = TypeHandle::FromPtr(PTR_VOID((TADDR)EHClause.TypeHandle)).IsObjectType(); + } + else + { + // the module of the token (whether a ref or def token) is the same as the module of the method containing the EH clause + deh.moduleAddr = HOST_CDADDR(codeInfo.GetMethodDesc()->GetModule()); + deh.tokCatch = EHClause.ClassToken; + TypeHandle th = ClassLoader::LookupTypeDefOrRefInModule(codeInfo.GetMethodDesc()->GetModule(), (mdToken)EHClause.ClassToken); + deh.isCatchAllHandler = th.IsObjectType(); + } } else { deh.clauseType = EHUnknown; } - if (HasCachedTypeHandle(&EHClause)) - { - deh.mtCatch = TO_CDADDR(&EHClause.TypeHandle); - } - else if(!IsFaultOrFinally(&EHClause)) - { - // the module of the token (whether a ref or def token) is the same as the module of the method containing the EH clause - deh.moduleAddr = HOST_CDADDR(codeInfo.GetMethodDesc()->GetModule()); - deh.tokCatch = EHClause.ClassToken; - } - deh.tryStartOffset = EHClause.TryStartPC; deh.tryEndOffset = EHClause.TryEndPC; deh.handlerStartOffset = EHClause.HandlerStartPC; diff --git a/src/coreclr/inc/eexcp.h b/src/coreclr/inc/eexcp.h index 5c470b78dbf527..110f11d8095ef8 100644 --- a/src/coreclr/inc/eexcp.h +++ b/src/coreclr/inc/eexcp.h @@ -67,7 +67,7 @@ struct EE_ILEXCEPTION : public COR_ILMETHOD_SECT_FAT } }; -#define COR_ILEXCEPTION_CLAUSE_CACHED_CLASS 0x10000000 +#define COR_ILEXCEPTION_CLAUSE_CACHED_CLASS 0x10000000 // [cDAC] [ExecutionManager] : Contract depends on this value. inline BOOL HasCachedTypeHandle(EE_ILEXCEPTION_CLAUSE *EHClause) { diff --git a/src/coreclr/inc/readytorun.h b/src/coreclr/inc/readytorun.h index 3d5032e23c24d8..37dad7c9aa4daf 100644 --- a/src/coreclr/inc/readytorun.h +++ b/src/coreclr/inc/readytorun.h @@ -98,7 +98,7 @@ enum class ReadyToRunSectionType : uint32_t ImportSections = 101, RuntimeFunctions = 102, MethodDefEntryPoints = 103, - ExceptionInfo = 104, + ExceptionInfo = 104, // [cDAC] [ExecutionManager] : Contract depends on this value. DebugInfo = 105, DelayLoadMethodCallThunks = 106, // 107 used by an older format of AvailableTypes diff --git a/src/coreclr/interpreter/compiler.cpp b/src/coreclr/interpreter/compiler.cpp index aa7282f0cbb3d4..10c649fa958f13 100644 --- a/src/coreclr/interpreter/compiler.cpp +++ b/src/coreclr/interpreter/compiler.cpp @@ -3096,7 +3096,6 @@ void InterpCompiler::EmitBinaryArithmeticOp(int32_t opBase) } else { -#if TARGET_64BIT if (type1 == StackTypeI8 && type2 == StackTypeI4) { EmitConv(m_pStackPointer - 1, StackTypeI8, InterpOpForWideningArgForImplicitUpcast((InterpOpcode)opBase)); @@ -3107,7 +3106,6 @@ void InterpCompiler::EmitBinaryArithmeticOp(int32_t opBase) EmitConv(m_pStackPointer - 2, StackTypeI8, InterpOpForWideningArgForImplicitUpcast((InterpOpcode)opBase)); type1 = StackTypeI8; } -#endif if (type1 == StackTypeR8 && type2 == StackTypeR4) { EmitConv(m_pStackPointer - 1, StackTypeR8, INTOP_CONV_R8_R4); diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index 8472dfe8c0233d..6b2ca6af8484d4 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -332,8 +332,11 @@ class CodeGen final : public CodeGenInterface // Prolog functions and data (there are a few exceptions for more generally used things) // - void genEstablishFramePointer(int delta, bool reportUnwindData); - void genHomeRegisterParams(regNumber initReg, bool* initRegStillZeroed); + void genEstablishFramePointer(int delta, bool reportUnwindData); + void genHomeRegisterParams(regNumber initReg, bool* initRegStillZeroed); +#ifdef TARGET_WASM + void genHomeRegisterParamsOutsideProlog(); +#endif regMaskTP genGetParameterHomingTempRegisterCandidates(); var_types genParamStackType(LclVarDsc* dsc, const ABIPassingSegment& seg); diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index 7823547e9edbe2..2be7f9c272edad 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -410,6 +410,16 @@ void CodeGen::genCodeForBBlist() } #endif +#ifdef TARGET_WASM + // genHomeRegisterParams can generate arbitrary amounts of code on Wasm, so + // we have moved it out of the prolog to the first basic block in order to + // work around the restriction that the prolog can only be one insGroup. + if (block->IsFirst()) + { + genHomeRegisterParamsOutsideProlog(); + } +#endif + #ifndef TARGET_WASM // TODO-WASM: enable genPoisonFrame // Emit poisoning into the init BB that comes right after prolog. // We cannot emit this code in the prolog as it might make the prolog too large. diff --git a/src/coreclr/jit/codegenloongarch64.cpp b/src/coreclr/jit/codegenloongarch64.cpp index feb4d28396a082..d64235083b03e9 100644 --- a/src/coreclr/jit/codegenloongarch64.cpp +++ b/src/coreclr/jit/codegenloongarch64.cpp @@ -557,14 +557,6 @@ void CodeGen::genCaptureFuncletPrologEpilogInfo() { delta_PSP -= TARGET_POINTER_SIZE; } - if ((m_compiler->lvaAsyncExecutionContextVar != BAD_VAR_NUM) && !m_compiler->opts.IsOSR()) - { - delta_PSP -= TARGET_POINTER_SIZE; - } - if ((m_compiler->lvaAsyncSynchronizationContextVar != BAD_VAR_NUM) && !m_compiler->opts.IsOSR()) - { - delta_PSP -= TARGET_POINTER_SIZE; - } funcletFrameSize = funcletFrameSize - delta_PSP; funcletFrameSize = roundUp((unsigned)funcletFrameSize, STACK_ALIGN); @@ -2279,7 +2271,7 @@ void CodeGen::genJumpTable(GenTree* treeNode) // Access to inline data is 'abstracted' by a special type of static member // (produced by eeFindJitDataOffs) which the emitter recognizes as being a reference // to constant data, not a real static field. - GetEmitter()->emitIns_R_C(INS_bl, emitActualTypeSize(TYP_I_IMPL), treeNode->GetRegNum(), REG_NA, + GetEmitter()->emitIns_R_C(INS_bl, EA_PTRSIZE, treeNode->GetRegNum(), REG_NA, m_compiler->eeFindJitDataOffs(jmpTabBase), 0); genProduceReg(treeNode); } @@ -2292,7 +2284,16 @@ void CodeGen::genJumpTable(GenTree* treeNode) // void CodeGen::genAsyncResumeInfo(GenTreeVal* treeNode) { - GetEmitter()->emitIns_R_C(INS_bl, emitActualTypeSize(TYP_I_IMPL), treeNode->GetRegNum(), REG_NA, + // INS_bl/b are placeholders that is not the final instruction. + instruction ins = INS_bl; + emitAttr attr = EA_PTRSIZE; + if (m_compiler->eeDataWithCodePointersNeedsRelocs()) + { + ins = INS_b; + attr = EA_SET_FLG(EA_PTRSIZE, EA_CNS_RELOC_FLG); + } + + GetEmitter()->emitIns_R_C(ins, attr, treeNode->GetRegNum(), REG_NA, genEmitAsyncResumeInfo((unsigned)treeNode->gtVal1), 0); genProduceReg(treeNode); } @@ -3341,8 +3342,17 @@ void CodeGen::genCodeForCompare(GenTreeOp* tree) } else { - emit->emitIns_I_la(EA_PTRSIZE, REG_RA, imm + 1); - emit->emitIns_R_R_R(IsUnsigned ? INS_sltu : INS_slt, EA_PTRSIZE, targetReg, regOp1, REG_RA); + assert(!(!IsUnsigned && (imm == INT64_MAX))); + if (IsUnsigned && (imm == ~0)) + { + // unsigned (a <= ~0) is always true. + emit->emitIns_R_R_I(INS_addi_d, EA_PTRSIZE, targetReg, REG_R0, 1); + } + else + { + emit->emitIns_I_la(EA_PTRSIZE, REG_RA, imm + 1); + emit->emitIns_R_R_R(IsUnsigned ? INS_sltu : INS_slt, EA_PTRSIZE, targetReg, regOp1, REG_RA); + } } } else if (tree->OperIs(GT_GT)) @@ -3711,14 +3721,6 @@ int CodeGenInterface::genSPtoFPdelta() const { delta -= TARGET_POINTER_SIZE; } - if ((m_compiler->lvaAsyncExecutionContextVar != BAD_VAR_NUM) && !m_compiler->opts.IsOSR()) - { - delta -= TARGET_POINTER_SIZE; - } - if ((m_compiler->lvaAsyncSynchronizationContextVar != BAD_VAR_NUM) && !m_compiler->opts.IsOSR()) - { - delta -= TARGET_POINTER_SIZE; - } assert(delta >= 0); return delta; @@ -6089,41 +6091,15 @@ void CodeGen::genCreateAndStoreGCInfo(unsigned codeSize, // Now we can actually use those slot ID's to declare live ranges. gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_DO_WORK, &callCnt); +#ifdef FEATURE_REMAP_FUNCTION if (m_compiler->opts.compDbgEnC) { - // what we have to preserve is called the "frame header" (see comments in VM\eetwain.cpp) - // which is: - // -return address - // -saved off RBP - // -saved 'this' pointer and bool for synchronized methods - - // 4 slots for RBP + return address + RSI + RDI - int preservedAreaSize = 4 * REGSIZE_BYTES; - - if (m_compiler->info.compFlags & CORINFO_FLG_SYNCH) - { - if (!(m_compiler->info.compFlags & CORINFO_FLG_STATIC)) - { - preservedAreaSize += REGSIZE_BYTES; - } - - preservedAreaSize += 1; // bool for synchronized methods - } - - if (m_compiler->lvaAsyncExecutionContextVar != BAD_VAR_NUM) - { - preservedAreaSize += TARGET_POINTER_SIZE; - } - - if (m_compiler->lvaAsyncSynchronizationContextVar != BAD_VAR_NUM) - { - preservedAreaSize += TARGET_POINTER_SIZE; - } - - // Used to signal both that the method is compiled for EnC, and also the size of the block at the top of the - // frame - gcInfoEncoder->SetSizeOfEditAndContinuePreservedArea(preservedAreaSize); + // TODO: lvaMonAcquired, lvaAsyncExecutionContextVar and lvaAsyncExecutionContextVar locals are special + // that is necessary to allocate in the top of the stack frame and included as part of the EnC frame header + // for EnC to work. + NYI_LOONGARCH64("compDbgEnc in CodeGen::genCreateAndStoreGCInfo() ---unimplemented/unused on LA64 yet---"); } +#endif // FEATURE_REMAP_FUNCTION if (m_compiler->opts.IsReversePInvoke()) { @@ -6765,14 +6741,6 @@ void CodeGen::genPushCalleeSavedRegisters(regNumber initReg, bool* pInitRegZeroe { localFrameSize -= TARGET_POINTER_SIZE; } - if ((m_compiler->lvaAsyncExecutionContextVar != BAD_VAR_NUM) && !m_compiler->opts.IsOSR()) - { - localFrameSize -= TARGET_POINTER_SIZE; - } - if ((m_compiler->lvaAsyncSynchronizationContextVar != BAD_VAR_NUM) && !m_compiler->opts.IsOSR()) - { - localFrameSize -= TARGET_POINTER_SIZE; - } #ifdef DEBUG if (m_compiler->opts.disAsm) @@ -6839,14 +6807,6 @@ void CodeGen::genPopCalleeSavedRegisters(bool jmpEpilog) { localFrameSize -= TARGET_POINTER_SIZE; } - if ((m_compiler->lvaAsyncExecutionContextVar != BAD_VAR_NUM) && !m_compiler->opts.IsOSR()) - { - localFrameSize -= TARGET_POINTER_SIZE; - } - if ((m_compiler->lvaAsyncSynchronizationContextVar != BAD_VAR_NUM) && !m_compiler->opts.IsOSR()) - { - localFrameSize -= TARGET_POINTER_SIZE; - } JITDUMP("Frame type. #outsz=%d; #framesz=%d; #calleeSaveRegsPushed:%d; " "localloc? %s\n", diff --git a/src/coreclr/jit/codegenwasm.cpp b/src/coreclr/jit/codegenwasm.cpp index 4dc8af8418ac9b..32f5ef4cdb950b 100644 --- a/src/coreclr/jit/codegenwasm.cpp +++ b/src/coreclr/jit/codegenwasm.cpp @@ -121,6 +121,22 @@ void CodeGen::genEnregisterOSRArgsAndLocals() //------------------------------------------------------------------------ // genHomeRegisterParams: place register arguments into their RA-assigned locations. // +// We can't actually do this task here because the prolog will overflow. Instead, we +// do this later on and inject all the relevant code into the first basic block. +// See genHomeRegisterParamsOutsideProlog, below. +// +// Arguments: +// initReg - Unused +// initRegStillZeroed - Unused +// +void CodeGen::genHomeRegisterParams(regNumber initReg, bool* initRegStillZeroed) +{ + // Intentionally empty +} + +//------------------------------------------------------------------------ +// genHomeRegisterParamsOutsideProlog: place register arguments into their RA-assigned locations. +// // For the WASM RA, we have a much simplified (compared to LSRA) contract of: // - If an argument is live on entry in a set of registers, then the RA will // assign those registers to that argument on entry. @@ -129,14 +145,9 @@ void CodeGen::genEnregisterOSRArgsAndLocals() // The main motivation for this (along with the obvious CQ implications) is // obviating the need to adapt the general "RegGraph"-based algorithm to // !HAS_FIXED_REGISTER_SET constraints (no reg masks). -// -// Arguments: -// initReg - Unused -// initRegStillZeroed - Unused -// -void CodeGen::genHomeRegisterParams(regNumber initReg, bool* initRegStillZeroed) +void CodeGen::genHomeRegisterParamsOutsideProlog() { - JITDUMP("*************** In genHomeRegisterParams()\n"); + JITDUMP("*************** In genHomeRegisterParamsOutsideProlog()\n"); auto spillParam = [this](unsigned lclNum, unsigned offset, unsigned paramLclNum, const ABIPassingSegment& segment) { assert(segment.IsPassedInRegister()); diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 77758b9f3cb9ce..570dc36f1a6b0a 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -10808,6 +10808,10 @@ void Compiler::EnregisterStats::RecordLocal(const LclVarDsc* varDsc) m_externallyVisibleImplicitly++; break; + case AddressExposedReason::SMALL_TYPE_PARTIAL_DEF: + m_smallTypePartialDef++; + break; + default: unreached(); break; @@ -10904,5 +10908,6 @@ void Compiler::EnregisterStats::Dump(FILE* fout) const PRINT_STATS(m_dispatchRetBuf, m_addrExposed); PRINT_STATS(m_stressPoisonImplicitByrefs, m_addrExposed); PRINT_STATS(m_externallyVisibleImplicitly, m_addrExposed); + PRINT_STATS(m_smallTypePartialDef, m_addrExposed); } #endif // TRACK_ENREG_STATS diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index d9e256c164e345..cb1137a8b4d0c5 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -502,6 +502,8 @@ enum class AddressExposedReason EXTERNALLY_VISIBLE_IMPLICITLY, // Local is visible externally without explicit escape in JIT IR. // For example because it is used by GC or is the outgoing arg area // that belongs to callees. + SMALL_TYPE_PARTIAL_DEF, // A small-typed local has a partial def that doesn't cover the full + // local, so we must treat it as normalize-on-load. }; #endif // DEBUG @@ -4210,6 +4212,17 @@ class Compiler unsigned lvaLclExactSize(unsigned varNum); ValueSize lvaLclValueSize(unsigned varNum); + //----------------------------------------------------------------------------- + // lvaIsUnknownSizeLocal: Does the local have an unknown size at compile-time? + // + // Returns: + // True if the local does not have an exact size, else false. + // + bool lvaIsUnknownSizeLocal(unsigned varNum) + { + return !lvaLclValueSize(varNum).IsExact(); + } + bool lvaHaveManyLocals(float percent = 1.0f) const; unsigned lvaGrabTemp(bool shortLifetime DEBUGARG(const char* reason)); @@ -6878,6 +6891,8 @@ class Compiler public: bool fgIsBigOffset(size_t offset); bool IsValidLclAddr(unsigned lclNum, unsigned offset); + bool IsEntireAccess(unsigned lclNum, unsigned offset, ValueSize accessSize); + bool IsWideAccess(unsigned lclNum, unsigned offset, ValueSize accessSize); bool IsPotentialGCSafePoint(GenTree* tree) const; private: @@ -11447,6 +11462,7 @@ class Compiler unsigned m_wideIndir; unsigned m_stressPoisonImplicitByrefs; unsigned m_externallyVisibleImplicitly; + unsigned m_smallTypePartialDef; public: void RecordLocal(const LclVarDsc* varDsc); diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index 2a618c5c2f1232..4484a8ae95075c 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -3324,7 +3324,7 @@ inline bool Compiler::fgIsBigOffset(size_t offset) } //------------------------------------------------------------------------ -// IsValidLclAddr: Can the given local address be represented as "LCL_FLD_ADDR"? +// IsValidLclAddr: Can the given local address be represented as "LCL_ADDR"? // // Local address nodes cannot point beyond the local and can only store // 16 bits worth of offset. @@ -3334,19 +3334,72 @@ inline bool Compiler::fgIsBigOffset(size_t offset) // offset - The address' offset // // Return Value: -// Whether "LCL_FLD_ADDR [+offset]" would be valid IR. +// Whether "LCL_ADDR [+offset]" would be valid IR. // inline bool Compiler::IsValidLclAddr(unsigned lclNum, unsigned offset) { #ifdef TARGET_ARM64 - if (varTypeHasUnknownSize(lvaGetDesc(lclNum))) + if (lvaIsUnknownSizeLocal(lclNum)) { - return false; + return (offset == 0); } #endif return (offset < UINT16_MAX) && (offset < lvaLclExactSize(lclNum)); } +//------------------------------------------------------------------------ +// IsEntireAccess: Is the access to a local entire? +// +// The access is entire when the size of the access is equivalent to the size +// of the local, and the access address is equivalent to the local address +// (offset == 0). +// +// Arguments: +// lclNum - The local's number +// offset - The access offset +// accessSize - The size of the access (may be unknown at compile-time) +// +// Return Value: +// True is the access is entire by the definition above, else false. +// +inline bool Compiler::IsEntireAccess(unsigned lclNum, unsigned offset, ValueSize accessSize) +{ + return (lvaLclValueSize(lclNum) == accessSize) && (offset == 0); +} + +//------------------------------------------------------------------------ +// IsWideAccess: Is the access to a local wide? +// +// An access is wide when the access overflows the end of the local. If the +// access size is unknown, the access is assumed wide if it is not entire. +// +// Arguments: +// lclNum - The local's number +// offset - The access offset +// accessSize - The size of the access (may be unknown at compile-time) +// +// Return Value: +// True is the access is wide by the definition above, else false. +// +inline bool Compiler::IsWideAccess(unsigned lclNum, unsigned offset, ValueSize accessSize) +{ + assert(!accessSize.IsNull()); + if (accessSize.IsExact()) + { + ClrSafeInt extent = ClrSafeInt(offset) + ClrSafeInt(accessSize.GetExact()); + // The access is wide if: + // * The offset computation overflows uint16_t. + // * The address at `offset + accessSize - 1`, (the last byte of the access) is out of bounds of the local. + return extent.IsOverflow() || !FitsIn(extent.Value()) || !IsValidLclAddr(lclNum, extent.Value() - 1); + } + else + { + // If we don't know the size of the access or the local at compile time, we assume any access overflows if + // it is not an entire access to the local. + return !IsEntireAccess(lclNum, offset, accessSize); + } +} + //------------------------------------------------------------------------ // IsPotentialGCSafePoint: Can the given tree be effectively a gc safe point? // diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp index 3eafb5c5bc9344..e620d149ca10b6 100644 --- a/src/coreclr/jit/emit.cpp +++ b/src/coreclr/jit/emit.cpp @@ -9765,7 +9765,8 @@ insGroup* emitter::emitAllocIG() ig = (insGroup*)emitGetMem(sz); #ifdef DEBUG - ig->igSelf = ig; + ig->igSelf = ig; + ig->igDataSize = 0; #endif #if EMITTER_STATS @@ -9832,6 +9833,8 @@ void emitter::emitInitIG(insGroup* ig) // Explicitly call init, since IGs don't actually have a constructor. ig->igBlocks.jitstd::list::init(m_compiler->getAllocator(CMK_DebugOnly)); #endif + + ig->igData = nullptr; } /***************************************************************************** diff --git a/src/coreclr/jit/emitloongarch64.cpp b/src/coreclr/jit/emitloongarch64.cpp index 83d6425480678e..6f752c830ba97f 100644 --- a/src/coreclr/jit/emitloongarch64.cpp +++ b/src/coreclr/jit/emitloongarch64.cpp @@ -1972,8 +1972,8 @@ void emitter::emitIns_R_R_R_R( /***************************************************************************** * * Add an instruction with a register + static member operands. - * Constant is stored into JIT data which is adjacent to code. - * For LOONGARCH64, maybe not the best, here just supports the func-interface. + * Usually constants are stored into JIT data adjacent to code, in which case no + * relocation is needed. PC-relative offset will be encoded directly into instruction. * */ void emitter::emitIns_R_C( @@ -1982,10 +1982,13 @@ void emitter::emitIns_R_C( assert(offs >= 0); assert(instrDesc::fitsInSmallCns(offs)); // can optimize. - // when id->idIns == bl, for reloc! 4-ins. + // when id->idIns == b, for AsyncResumeInfo reloc. + // pcalau12i reg, off-hi-20bits + // addi_d reg, offs_lo-12bits(reg) + // when id->idIns == bl, for reloc! 2-ins. // pcaddu12i reg, off-hi-20bits // addi_d reg, reg, off-lo-12bits - // when id->idIns == load-ins, for reloc! 4-ins. + // when id->idIns == load-ins, for reloc! 2-ins. // pcaddu12i reg, off-hi-20bits // load reg, offs_lo-12bits(reg) // @@ -2000,6 +2003,7 @@ void emitter::emitIns_R_C( // load reg, r21 + addr_bits[11:0] instrDesc* id = emitNewInstr(attr); + id->idSetRelocFlags(attr); id->idIns(ins); assert(reg != REG_R0); // for special. reg Must not be R0. @@ -2007,7 +2011,7 @@ void emitter::emitIns_R_C( id->idSmallCns(offs); // usually is 0. id->idInsOpt(INS_OPTS_RC); - if (m_compiler->opts.compReloc) + if (m_compiler->opts.compReloc || id->idIsReloc()) { id->idSetIsDspReloc(); id->idCodeSize(8); @@ -2030,7 +2034,6 @@ void emitter::emitIns_R_C( id->idOpSize(EA_PTRSIZE); } - // TODO-LoongArch64: this maybe deleted. id->idSetIsBound(); // We won't patch address since we will know the exact distance // once JIT code and data are allocated together. @@ -3376,6 +3379,9 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) { // Reference to JIT data + // when id->idIns == b, for AsyncResumeInfo reloc. + // pcalau12i reg, off-hi-20bits + // addi_d reg, offs_lo-12bits(reg) // when id->idIns == bl, for reloc! // pcaddu12i r21, off-hi-20bits // addi_d reg, r21, off-lo-12bits @@ -3408,7 +3414,18 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) ins = id->idIns(); regNumber reg1 = id->idReg1(); - if (id->idIsReloc()) + if (ins == INS_b) + { + // relocation for AsyncResumeInfo. + *(code_t*)dstRW = 0x1a000000 | (code_t)reg1; + dstRW += 4; + ins = INS_addi_d; + *(code_t*)dstRW = 0x02c00000 | (code_t)reg1 | (code_t)(reg1 << 5); + dstRW += 4; + emitRecordRelocation(dstRW - 8 - writeableOffset, emitDataOffsetToPtr(dataOffs), + CorInfoReloc::LOONGARCH64_PC); + } + else if (id->idIsReloc()) { // get the addr-offset of the data. imm = (ssize_t)emitDataOffsetToPtr(dataOffs) - (ssize_t)(dstRW - writeableOffset); diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 3dd134781e5d39..3dfca0ba725a0a 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -7358,9 +7358,6 @@ ExceptionSetFlags GenTree::OperExceptions(Compiler* comp) case GT_CKFINITE: return ExceptionSetFlags::ArithmeticException; - case GT_LCLHEAP: - return ExceptionSetFlags::StackOverflowException; - #ifdef FEATURE_HW_INTRINSICS case GT_HWINTRINSIC: { diff --git a/src/coreclr/jit/hwintrinsiclistarm64sve.h b/src/coreclr/jit/hwintrinsiclistarm64sve.h index 4f26155747e3f4..06ddd231b5ed74 100644 --- a/src/coreclr/jit/hwintrinsiclistarm64sve.h +++ b/src/coreclr/jit/hwintrinsiclistarm64sve.h @@ -131,10 +131,10 @@ HARDWARE_INTRINSIC(Sve, GatherVectorUInt16WithByteOffsetsZeroExtend, HARDWARE_INTRINSIC(Sve, GatherVectorUInt16WithByteOffsetsZeroExtendFirstFaulting, -1, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ldff1h, INS_sve_ldff1h, INS_sve_ldff1h, INS_sve_ldff1h, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_ZeroingMaskedOperation|HW_Flag_SpecialSideEffect_Other) HARDWARE_INTRINSIC(Sve, GatherVectorUInt16ZeroExtend, -1, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ld1h, INS_sve_ld1h, INS_sve_ld1h, INS_sve_ld1h, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_ZeroingMaskedOperation) HARDWARE_INTRINSIC(Sve, GatherVectorUInt16ZeroExtendFirstFaulting, -1, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ldff1h, INS_sve_ldff1h, INS_sve_ldff1h, INS_sve_ldff1h, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_ZeroingMaskedOperation|HW_Flag_SpecialSideEffect_Other) -HARDWARE_INTRINSIC(Sve, GatherVectorUInt32WithByteOffsetsZeroExtend, -1, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ld1w, INS_sve_ld1w, INS_sve_ld1w, INS_sve_ld1w, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_ZeroingMaskedOperation) -HARDWARE_INTRINSIC(Sve, GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting, -1, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ldff1w, INS_sve_ldff1w, INS_sve_ldff1w, INS_sve_ldff1w, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_ZeroingMaskedOperation|HW_Flag_SpecialSideEffect_Other) -HARDWARE_INTRINSIC(Sve, GatherVectorUInt32ZeroExtend, -1, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ld1w, INS_sve_ld1w, INS_sve_ld1w, INS_sve_ld1w, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_ZeroingMaskedOperation) -HARDWARE_INTRINSIC(Sve, GatherVectorUInt32ZeroExtendFirstFaulting, -1, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ldff1w, INS_sve_ldff1w, INS_sve_ldff1w, INS_sve_ldff1w, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_ZeroingMaskedOperation|HW_Flag_SpecialSideEffect_Other) +HARDWARE_INTRINSIC(Sve, GatherVectorUInt32WithByteOffsetsZeroExtend, -1, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ld1w, INS_sve_ld1w, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_ZeroingMaskedOperation) +HARDWARE_INTRINSIC(Sve, GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting, -1, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ldff1w, INS_sve_ldff1w, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_ZeroingMaskedOperation|HW_Flag_SpecialSideEffect_Other) +HARDWARE_INTRINSIC(Sve, GatherVectorUInt32ZeroExtend, -1, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ld1w, INS_sve_ld1w, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_ZeroingMaskedOperation) +HARDWARE_INTRINSIC(Sve, GatherVectorUInt32ZeroExtendFirstFaulting, -1, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ldff1w, INS_sve_ldff1w, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_ZeroingMaskedOperation|HW_Flag_SpecialSideEffect_Other) HARDWARE_INTRINSIC(Sve, GatherVectorWithByteOffsetFirstFaulting, -1, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ldff1w, INS_sve_ldff1w, INS_sve_ldff1d, INS_sve_ldff1d, INS_sve_ldff1w, INS_sve_ldff1d}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_ZeroingMaskedOperation|HW_Flag_SpecialSideEffect_Other) HARDWARE_INTRINSIC(Sve, GatherVectorWithByteOffsets, -1, 3, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ld1w, INS_sve_ld1w, INS_sve_ld1d, INS_sve_ld1d, INS_sve_ld1w, INS_sve_ld1d}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_ZeroingMaskedOperation) HARDWARE_INTRINSIC(Sve, GetActiveElementCount, -1, 2, {INS_sve_cntp, INS_sve_cntp, INS_sve_cntp, INS_sve_cntp, INS_sve_cntp, INS_sve_cntp, INS_sve_cntp, INS_sve_cntp, INS_sve_cntp, INS_sve_cntp}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_ExplicitMaskedOperation) diff --git a/src/coreclr/jit/ifconversion.cpp b/src/coreclr/jit/ifconversion.cpp index 1b49c438aab9ad..0b9e11c962e8fb 100644 --- a/src/coreclr/jit/ifconversion.cpp +++ b/src/coreclr/jit/ifconversion.cpp @@ -55,7 +55,6 @@ class OptIfConversionDsc bool IfConvertCheckThenFlow(); void IfConvertFindFlow(); bool IfConvertCheckStmts(BasicBlock* fromBlock, IfConvertOperation* foundOperation); - void IfConvertJoinStmts(BasicBlock* fromBlock); GenTree* TryTransformSelectOperOrLocal(GenTree* oper, GenTree* lcl); GenTree* TryTransformSelectOperOrZero(GenTree* oper, GenTree* lcl); @@ -356,26 +355,6 @@ bool OptIfConversionDsc::IfConvertCheckStmts(BasicBlock* fromBlock, IfConvertOpe return found; } -//----------------------------------------------------------------------------- -// IfConvertJoinStmts -// -// Move all the statements from a block onto the end of the start block. -// -// Arguments: -// fromBlock -- Source block -// -void OptIfConversionDsc::IfConvertJoinStmts(BasicBlock* fromBlock) -{ - Statement* stmtList1 = m_startBlock->firstStmt(); - Statement* stmtList2 = fromBlock->firstStmt(); - Statement* stmtLast1 = m_startBlock->lastStmt(); - Statement* stmtLast2 = fromBlock->lastStmt(); - stmtLast1->SetNextStmt(stmtList2); - stmtList2->SetPrevStmt(stmtLast1); - stmtList1->SetPrevStmt(stmtLast2); - fromBlock->SetFirstStmt(nullptr); -} - //----------------------------------------------------------------------------- // IfConvertDump // @@ -384,19 +363,24 @@ void OptIfConversionDsc::IfConvertJoinStmts(BasicBlock* fromBlock) #ifdef DEBUG void OptIfConversionDsc::IfConvertDump() { - assert(m_startBlock != nullptr); m_compiler->fgDumpBlock(m_startBlock); - BasicBlock* dumpBlock = m_startBlock->KindIs(BBJ_COND) ? m_startBlock->GetFalseTarget() : m_startBlock->GetTarget(); - for (; dumpBlock != m_finalBlock; dumpBlock = dumpBlock->GetUniqueSucc()) - { - m_compiler->fgDumpBlock(dumpBlock); - } - if (m_doElseConversion) + + bool beforeTransformation = m_startBlock->KindIs(BBJ_COND); + if (beforeTransformation) { - dumpBlock = m_startBlock->KindIs(BBJ_COND) ? m_startBlock->GetTrueTarget() : m_startBlock->GetTarget(); - for (; dumpBlock != m_finalBlock; dumpBlock = dumpBlock->GetUniqueSucc()) + // Dump all Then blocks + for (BasicBlock* bb = m_startBlock->GetFalseTarget(); bb != m_finalBlock; bb = bb->GetUniqueSucc()) + { + m_compiler->fgDumpBlock(bb); + } + + if (m_doElseConversion) { - m_compiler->fgDumpBlock(dumpBlock); + // Dump all Else blocks + for (BasicBlock* bb = m_startBlock->GetTrueTarget(); bb != m_finalBlock; bb = bb->GetUniqueSucc()) + { + m_compiler->fgDumpBlock(bb); + } } } } @@ -731,9 +715,8 @@ bool OptIfConversionDsc::optIfConvert(int* pReachabilityBudget) select = m_compiler->gtNewConditionalNode(GT_SELECT, m_cond, selectTrueInput, selectFalseInput, selectType); } + // Use the SELECT as the source of the Then STORE/RETURN. m_thenOperation.node->AddAllEffectsFlags(select); - - // Use the select as the source of the Then operation. if (m_mainOper == GT_STORE_LCL_VAR) { m_thenOperation.node->AsLclVar()->Data() = select; @@ -745,30 +728,47 @@ bool OptIfConversionDsc::optIfConvert(int* pReachabilityBudget) m_compiler->gtSetEvalOrder(m_thenOperation.node); m_compiler->fgSetStmtSeq(m_thenOperation.stmt); - // Remove statements. - last->gtBashToNOP(); - m_compiler->gtSetEvalOrder(last); - m_compiler->fgSetStmtSeq(m_startBlock->lastStmt()); - if (m_doElseConversion) + // Replace JTRUE with STORE(SELECT)/RETURN(SELECT) statement + m_compiler->fgInsertStmtBefore(m_startBlock, m_startBlock->lastStmt(), m_thenOperation.stmt); + m_compiler->fgRemoveStmt(m_startBlock, m_startBlock->lastStmt()); + m_thenOperation.block->SetFirstStmt(nullptr); + + BasicBlock* falseBb = m_startBlock->GetFalseTarget(); + BasicBlock* trueBb = m_startBlock->GetTrueTarget(); + + // JTRUE block now contains SELECT. Change it's kind and make it flow + // directly into block where flows merge, which is null in case of GT_RETURN. + if (m_mainOper == GT_RETURN) { - m_elseOperation.node->gtBashToNOP(); - m_compiler->gtSetEvalOrder(m_elseOperation.node); - m_compiler->fgSetStmtSeq(m_elseOperation.stmt); + m_startBlock->SetKindAndTargetEdge(BBJ_RETURN); } + else + { + FlowEdge* newEdge = + m_doElseConversion ? m_compiler->fgAddRefPred(m_finalBlock, m_startBlock) : m_startBlock->GetTrueEdge(); + m_startBlock->SetKindAndTargetEdge(BBJ_ALWAYS, newEdge); + } + assert(m_startBlock->GetUniqueSucc() == m_finalBlock); - // Merge all the blocks. - IfConvertJoinStmts(m_thenOperation.block); + // Remove all Then/Else blocks + auto removeBlocks = [&](BasicBlock* start) { + m_compiler->fgRemoveAllRefPreds(start, m_startBlock); + start->bbWeight = BB_ZERO_WEIGHT; + assert(start->bbPreds == nullptr); + + for (BasicBlock* bb = start; bb != m_finalBlock;) + { + BasicBlock* next = bb->GetUniqueSucc(); + m_compiler->fgRemoveBlock(bb, true); + bb = next; + } + }; + removeBlocks(falseBb); if (m_doElseConversion) { - IfConvertJoinStmts(m_elseOperation.block); + removeBlocks(trueBb); } - // Update the flow from the original block. - FlowEdge* const removedEdge = m_compiler->fgRemoveAllRefPreds(m_startBlock->GetFalseTarget(), m_startBlock); - FlowEdge* const retainedEdge = m_startBlock->GetTrueEdge(); - m_startBlock->SetKindAndTargetEdge(BBJ_ALWAYS, retainedEdge); - m_compiler->fgRepairProfileCondToUncond(m_startBlock, retainedEdge, removedEdge); - #ifdef DEBUG if (m_compiler->verbose) { diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 18670bb36147ee..7513b590f4cde4 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -10077,8 +10077,9 @@ void Compiler::impImportBlockCode(BasicBlock* block) } op1 = gtNewOperNode(GT_LCLHEAP, TYP_I_IMPL, op2); - // May throw a stack overflow exception. Obviously, we don't want locallocs to be CSE'd. - op1->gtFlags |= (GTF_EXCEPT | GTF_DONT_CSE); + // We do not model stack overflow from localloc as an exception side effect. + // Obviously, we don't want locallocs to be CSE'd. + op1->gtFlags |= GTF_DONT_CSE; // Request stack security for this method. setNeedsGSSecurityCookie(); diff --git a/src/coreclr/jit/lclmorph.cpp b/src/coreclr/jit/lclmorph.cpp index 8675ae841ed90f..8f86759cdd284d 100644 --- a/src/coreclr/jit/lclmorph.cpp +++ b/src/coreclr/jit/lclmorph.cpp @@ -1010,7 +1010,7 @@ class LocalAddressVisitor final : public GenTreeVisitor break; case GT_FIELD_ADDR: - if (MorphStructFieldAddress(node, 0) != BAD_VAR_NUM) + if (MorphStructFieldAddress(node, ValueSize(0)) != BAD_VAR_NUM) { goto LOCAL_NODE; } @@ -1595,32 +1595,10 @@ class LocalAddressVisitor final : public GenTreeVisitor unsigned lclNum = val.LclNum(); unsigned offset = val.Offset(); LclVarDsc* varDsc = m_compiler->lvaGetDesc(lclNum); - unsigned indirSize = node->AsIndir()->Size(); - bool isWide; + ValueSize lclSize = m_compiler->lvaLclValueSize(lclNum); + ValueSize indirSize = node->AsIndir()->ValueSize(); - // TODO-Cleanup: delete "indirSize == 0", use "Compiler::IsValidLclAddr". - if ((indirSize == 0) || ((offset + indirSize) > UINT16_MAX)) - { - // If we can't figure out the indirection size then treat it as a wide indirection. - // Additionally, treat indirections with large offsets as wide: local field nodes - // and the emitter do not support them. - isWide = true; - } - else - { - ClrSafeInt endOffset = ClrSafeInt(offset) + ClrSafeInt(indirSize); - - if (endOffset.IsOverflow()) - { - isWide = true; - } - else - { - isWide = endOffset.Value() > m_compiler->lvaLclExactSize(lclNum); - } - } - - if (isWide) + if (indirSize.IsNull() || m_compiler->IsWideAccess(lclNum, offset, indirSize)) { unsigned exposedLclNum = varDsc->lvIsStructField ? varDsc->lvParentLcl : lclNum; if (m_lclAddrAssertions != nullptr) @@ -1919,6 +1897,15 @@ class LocalAddressVisitor final : public GenTreeVisitor if (indir->IsPartialLclFld(m_compiler)) { lclNodeFlags |= GTF_VAR_USEASG; + + // A partial def of a small-typed local can leave upper bits in an + // incorrect state. Address-expose such locals to make them + // normalize-on-load, ensuring correct upper bits on every read. + LclVarDsc* varDsc = m_compiler->lvaGetDesc(lclNum); + if (varTypeIsSmall(varDsc->TypeGet()) && !varDsc->lvIsStructField) + { + m_compiler->lvaSetVarAddrExposed(lclNum DEBUGARG(AddressExposedReason::SMALL_TYPE_PARTIAL_DEF)); + } } } @@ -2065,7 +2052,7 @@ class LocalAddressVisitor final : public GenTreeVisitor return false; } - unsigned fieldLclNum = MorphStructFieldAddress(addr, node->Size()); + unsigned fieldLclNum = MorphStructFieldAddress(addr, node->ValueSize()); if (fieldLclNum == BAD_VAR_NUM) { return false; @@ -2104,13 +2091,13 @@ class LocalAddressVisitor final : public GenTreeVisitor // // Arguments: // node - the address node - // accessSize - load/store size if known, zero otherwise + // accessSize - load/store value size // // Return Value: // Local number for the promoted field if the replacement was successful, // BAD_VAR_NUM otherwise. // - unsigned MorphStructFieldAddress(GenTree* node, unsigned accessSize) + unsigned MorphStructFieldAddress(GenTree* node, ValueSize accessSize) { unsigned offset = 0; bool isSpanLength = false; @@ -2138,16 +2125,16 @@ class LocalAddressVisitor final : public GenTreeVisitor } LclVarDsc* fieldVarDsc = m_compiler->lvaGetDesc(fieldLclNum); + ValueSize fieldSize = fieldVarDsc->lvValueSize(); // Span's Length is never negative unconditionally - if (isSpanLength && (accessSize == genTypeSize(TYP_INT))) + if (isSpanLength && (accessSize.GetExact() == genTypeSize(TYP_INT))) { - fieldVarDsc->SetIsNeverNegative(true); + unsigned exactSize = accessSize.GetExact(); + unsigned exactFieldSize = fieldSize.GetExact(); } - // Retargeting the indirection to reference the promoted field would make it "wide", exposing - // the whole parent struct (with all of its fields). - if (accessSize > genTypeSize(fieldVarDsc)) + if (!accessSize.IsNull() && m_compiler->IsWideAccess(fieldLclNum, 0, accessSize)) { return BAD_VAR_NUM; } diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index 49e339fcbf479a..acd4be1edf083e 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -4352,24 +4352,26 @@ void Compiler::lvaFixVirtualFrameOffsets() if ((lvaMonAcquired != BAD_VAR_NUM) && !opts.IsOSR()) { - int offset = lvaTable[lvaMonAcquired].GetStackOffset() + delta; + int offset = lvaTable[lvaMonAcquired].GetStackOffset() + (compCalleeRegsPushed << 3); lvaTable[lvaMonAcquired].SetStackOffset(offset); delta += lvaLclStackHomeSize(lvaMonAcquired); } +#ifndef TARGET_LOONGARCH64 if ((lvaAsyncExecutionContextVar != BAD_VAR_NUM) && !opts.IsOSR()) { - int offset = lvaTable[lvaAsyncExecutionContextVar].GetStackOffset() + delta; + int offset = lvaTable[lvaAsyncExecutionContextVar].GetStackOffset() + (compCalleeRegsPushed << 3); lvaTable[lvaAsyncExecutionContextVar].SetStackOffset(offset); delta += lvaLclStackHomeSize(lvaAsyncExecutionContextVar); } if ((lvaAsyncSynchronizationContextVar != BAD_VAR_NUM) && !opts.IsOSR()) { - int offset = lvaTable[lvaAsyncSynchronizationContextVar].GetStackOffset() + delta; + int offset = lvaTable[lvaAsyncSynchronizationContextVar].GetStackOffset() + (compCalleeRegsPushed << 3); lvaTable[lvaAsyncSynchronizationContextVar].SetStackOffset(offset); delta += lvaLclStackHomeSize(lvaAsyncSynchronizationContextVar); } +#endif JITDUMP("--- delta bump %d for FP frame\n", delta); } diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 657e2576241a60..8e7976a0e1e9d3 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -4614,6 +4614,20 @@ GenTree* Lowering::OptimizeConstCompare(GenTree* cmp) op1->SupportsSettingFlagsAsCompareToZero())) && BlockRange().TryGetUse(cmp, &use) && IsProfitableToSetZeroFlag(op1)) { + // For unsigned compares against zero that rely on flags-as-compare-to-zero, + // we cannot use UGT/UGE/ULT/ULE directly because op1 may set only NZ flags + // (e.g. ANDS on ARM64 clears C/V). UGT/ULT depend on carry, which would be wrong. + // If we'd need to bail, do it before touching the LIR. + const bool isUnsignedZeroCompare = cmp->IsUnsigned() && op2->IsIntegralConst(0); + if (isUnsignedZeroCompare && cmp->OperIs(GT_GT, GT_GE, GT_LT, GT_LE)) + { + if (cmp->OperIs(GT_GE, GT_LT)) + { + // x >= 0U is always true and x < 0U is always false; keep the compare for correctness. + return cmp; + } + } + op1->gtFlags |= GTF_SET_FLAGS; op1->SetUnusedValue(); @@ -4623,11 +4637,8 @@ GenTree* Lowering::OptimizeConstCompare(GenTree* cmp) GenCondition cmpCondition = GenCondition::FromRelop(cmp); - // For unsigned compares against zero that rely on flags-as-compare-to-zero, - // we cannot use UGT/UGE/ULT/ULE directly because op1 may set only NZ flags - // (e.g. ANDS on ARM64 clears C/V). UGT/ULT depend on carry, which would be wrong. - // Rewrite the condition to use Z/N where possible, or bail out. - if (cmp->IsUnsigned() && op2->IsIntegralConst(0) && cmp->OperIs(GT_GT, GT_GE, GT_LT, GT_LE)) + // Use Z/N where possible. + if (isUnsignedZeroCompare) { if (cmp->OperIs(GT_GT)) { @@ -4639,11 +4650,6 @@ GenTree* Lowering::OptimizeConstCompare(GenTree* cmp) // x <= 0U <=> x == 0 cmpCondition = GenCondition::EQ; } - else - { - // x >= 0U is always true and x < 0U is always false; keep the compare for correctness. - return cmp; - } } GenTreeCC* setcc = m_compiler->gtNewCC(GT_SETCC, cmp->TypeGet(), cmpCondition); BlockRange().InsertAfter(op1, setcc); @@ -11703,7 +11709,7 @@ void Lowering::TransformUnusedIndirection(GenTreeIndir* ind, Compiler* m_compile // LowerLclHeap: a common logic to lower LCLHEAP. // // Arguments: -// blkNode - the LCLHEAP node we are lowering. +// node - the LCLHEAP node we are lowering. // void Lowering::LowerLclHeap(GenTree* node) { diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 0add5dd01529e4..0531aef7d53e6c 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -948,11 +948,12 @@ void CallArgs::ArgsComplete(Compiler* comp, GenTreeCall* call) #if !FEATURE_FIXED_OUT_ARGS // On x86 we previously recorded a stack depth of zero when // morphing the register arguments of any GT_IND with a GTF_IND_RNGCHK flag - // Thus we can not reorder the argument after any stack based argument - // (Note that GT_LCLHEAP sets the GTF_EXCEPT flag so we don't need to - // check for it explicitly.) + // Thus we can not reorder the argument after any stack based argument. + // GT_LCLHEAP has the same stack depth constraint, but it no longer sets + // GTF_EXCEPT, so it must be checked explicitly here. // - if (argx->gtFlags & GTF_EXCEPT) + if (((argx->gtFlags & GTF_EXCEPT) != 0) || + (comp->compLocallocUsed && comp->gtTreeContainsOper(argx, GT_LCLHEAP))) { SetNeedsTemp(&arg); continue; @@ -960,15 +961,11 @@ void CallArgs::ArgsComplete(Compiler* comp, GenTreeCall* call) #else // For Arm/X64 we can't reorder a register argument that uses a GT_LCLHEAP // - if (argx->gtFlags & GTF_EXCEPT) + if (comp->compLocallocUsed && comp->gtTreeContainsOper(argx, GT_LCLHEAP)) { assert(comp->compLocallocUsed); - - if (comp->gtTreeContainsOper(argx, GT_LCLHEAP)) - { - SetNeedsTemp(&arg); - continue; - } + SetNeedsTemp(&arg); + continue; } #endif } @@ -8496,12 +8493,11 @@ GenTree* Compiler::fgMorphFinalizeIndir(GenTreeIndir* indir) if (!indir->IsVolatile() && !indir->TypeIs(TYP_STRUCT) && addr->OperIs(GT_LCL_ADDR)) { - unsigned size = indir->Size(); - unsigned offset = addr->AsLclVarCommon()->GetLclOffs(); - unsigned extent = offset + size; - unsigned lclSize = lvaLclExactSize(addr->AsLclVarCommon()->GetLclNum()); + int lclNum = addr->AsLclVarCommon()->GetLclNum(); + unsigned offset = addr->AsLclVarCommon()->GetLclOffs(); + ValueSize indirSize = indir->ValueSize(); - if ((extent <= lclSize) && (extent < UINT16_MAX)) + if (!IsWideAccess(lclNum, offset, indirSize)) { addr->ChangeType(indir->TypeGet()); if (indir->OperIs(GT_STOREIND)) @@ -8582,6 +8578,19 @@ GenTree* Compiler::fgOptimizeCast(GenTreeCast* cast) var_types castToType = cast->CastToType(); + // For small-int casts fed by a widening int->long, remove the widening so we truncate directly + // from the original int value. + if (varTypeIsSmall(castToType) && src->OperIs(GT_CAST) && !src->gtOverflow()) + { + GenTreeCast* widening = src->AsCast(); + if (varTypeIsLong(widening->CastToType()) && (genActualType(widening->CastFromType()) == TYP_INT)) + { + cast->CastOp() = widening->CastOp(); + DEBUG_DESTROY_NODE(widening); + src = cast->CastOp(); + } + } + // For indir-like nodes, we may be able to change their type to satisfy (and discard) the cast. if (varTypeIsSmall(castToType) && (genTypeSize(castToType) == genTypeSize(src)) && src->OperIs(GT_IND, GT_LCL_FLD)) @@ -10400,30 +10409,31 @@ GenTree* Compiler::fgOptimizeAddition(GenTreeOp* add) if (opts.OptimizationEnabled()) { - // Reduce local addresses: "ADD(LCL_ADDR, OFFSET)" => "LCL_FLD_ADDR". + // Reduce local addresses: "ADD(LCL_ADDR(BASE), OFFSET)" => "LCL_ADDR(BASE+OFFSET)". // if (op1->OperIs(GT_LCL_ADDR) && op2->IsCnsIntOrI()) { GenTreeLclVarCommon* lclAddrNode = op1->AsLclVarCommon(); GenTreeIntCon* offsetNode = op2->AsIntCon(); - if (FitsIn(offsetNode->IconValue())) + ssize_t consVal = offsetNode->IconValue(); + + // Note: the emitter does not expect out-of-bounds access for LCL_ADDR. + if (FitsIn(consVal) && IsValidLclAddr(lclAddrNode->GetLclNum(), (uint32_t)consVal)) { - unsigned offset = lclAddrNode->GetLclOffs() + static_cast(offsetNode->IconValue()); + ClrSafeInt newOffset = + ClrSafeInt(lclAddrNode->GetLclOffs()) + ClrSafeInt(consVal); + assert(!newOffset.IsOverflow()); - // Note: the emitter does not expect out-of-bounds access for LCL_FLD_ADDR. - if (FitsIn(offset) && (offset < lvaLclExactSize(lclAddrNode->GetLclNum()))) - { - lclAddrNode->SetOper(GT_LCL_ADDR); - lclAddrNode->AsLclFld()->SetLclOffs(offset); - assert(lvaGetDesc(lclAddrNode)->lvDoNotEnregister); + lclAddrNode->SetOper(GT_LCL_ADDR); + lclAddrNode->AsLclFld()->SetLclOffs(newOffset.Value()); + assert(lvaGetDesc(lclAddrNode)->lvDoNotEnregister); - lclAddrNode->SetVNsFromNode(add); + lclAddrNode->SetVNsFromNode(add); - DEBUG_DESTROY_NODE(offsetNode); - DEBUG_DESTROY_NODE(add); + DEBUG_DESTROY_NODE(offsetNode); + DEBUG_DESTROY_NODE(add); - return lclAddrNode; - } + return lclAddrNode; } } diff --git a/src/coreclr/jit/utils.h b/src/coreclr/jit/utils.h index b410c5ce6abb67..38c819e0672896 100644 --- a/src/coreclr/jit/utils.h +++ b/src/coreclr/jit/utils.h @@ -588,8 +588,7 @@ enum class ExceptionSetFlags : uint32_t ArithmeticException = 0x4, NullReferenceException = 0x8, IndexOutOfRangeException = 0x10, - StackOverflowException = 0x20, - UnknownException = 0x40, + UnknownException = 0x20, }; class HelperCallProperties diff --git a/src/coreclr/nativeaot/Runtime/loongarch64/GcProbe.S b/src/coreclr/nativeaot/Runtime/loongarch64/GcProbe.S index 118af7c53ca1ab..e022a907caa1c0 100644 --- a/src/coreclr/nativeaot/Runtime/loongarch64/GcProbe.S +++ b/src/coreclr/nativeaot/Runtime/loongarch64/GcProbe.S @@ -4,10 +4,11 @@ #include #include "AsmOffsets.inc" -#define PROBE_FRAME_SIZE 0x90 // 4 * 8 for fixed part of PInvokeTransitionFrame (fp, ra, m_pThread, m_Flags) + +#define PROBE_FRAME_SIZE 0xA0 // 4 * 8 for fixed part of PInvokeTransitionFrame (fp, ra, m_pThread, m_Flags) + // 9 * 8 for callee saved registers + // 1 * 8 for caller SP + - // 2 * 8 for int returns + + // 3 * 8 for int returns (a0, a1, a2) + + // 1 * 8 for alignment padding + // 2 * 8 for FP returns // See PUSH_COOP_PINVOKE_FRAME, this macro is very similar, but also saves return registers @@ -37,13 +38,15 @@ // Slot at $sp+0x68 is reserved for caller sp - // Save the integer return registers + // Save the integer return registers, a2 might contain an objectref (async continuation) st.d $a0, $sp, 0x70 st.d $a1, $sp, 0x78 + st.d $a2, $sp, 0x80 + // Slot at [sp, #0x88] is alignment padding // Save the FP return registers - fst.d $f0, $sp, 0x80 - fst.d $f1, $sp, 0x88 + fst.d $f0, $sp, 0x90 + fst.d $f1, $sp, 0x98 // Perform the rest of the PInvokeTransitionFrame initialization. st.d \threadReg, $sp, OFFSETOF__PInvokeTransitionFrame__m_pThread // Thread * (unused by stackwalker) @@ -66,10 +69,11 @@ // Restore the integer return registers ld.d $a0, $sp, 0x70 ld.d $a1, $sp, 0x78 + ld.d $a2, $sp, 0x80 // Restore the FP return registers - fld.d $f0, $sp, 0x80 - fld.d $f1, $sp, 0x88 + fld.d $f0, $sp, 0x90 + fld.d $f1, $sp, 0x98 // Restore callee saved registers EPILOG_RESTORE_REG_PAIR 23, 24, 0x20 @@ -89,25 +93,26 @@ // All registers correct for return to the original return address. // // Register state on exit: -// a2: thread pointer +// a4: thread pointer +// a0, a1, a2: preserved // .macro FixupHijackedCallstack - // a2 <- GetThread() - INLINE_GETTHREAD $a2 + // a4 <- GetThread() + INLINE_GETTHREAD $a4 // // Fix the stack by restoring the original return address // // Load m_pvHijackedReturnAddress - ld.d $ra, $a2, OFFSETOF__Thread__m_pvHijackedReturnAddress + ld.d $ra, $a4, OFFSETOF__Thread__m_pvHijackedReturnAddress // // Clear hijack state // // Clear m_ppvHijackedReturnAddressLocation and m_pvHijackedReturnAddress - st.d $zero, $a2, OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation - st.d $zero, $a2, OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation + 8 + st.d $zero, $a4, OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation + st.d $zero, $a4, OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation + 8 .endm // @@ -122,16 +127,16 @@ NESTED_ENTRY RhpGcProbeHijack, _TEXT, NoHandler jirl $r0, $ra, 0 LOCAL_LABEL(WaitForGC): - li.d $t3, (DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_R4 + PTFF_SAVE_R5 + PTFF_THREAD_HIJACK) + li.d $t3, (DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_R4 + PTFF_SAVE_R5 + PTFF_SAVE_R6 + PTFF_THREAD_HIJACK) b C_FUNC(RhpWaitForGC) NESTED_END RhpGcProbeHijack .global C_FUNC(RhpThrowHwEx) NESTED_ENTRY RhpWaitForGC, _TEXT, NoHandler - PUSH_PROBE_FRAME $a2, $a3, $t3 + PUSH_PROBE_FRAME $a4, $a3, $t3 - ld.d $a0, $a2, OFFSETOF__Thread__m_pDeferredTransitionFrame + ld.d $a0, $a4, OFFSETOF__Thread__m_pDeferredTransitionFrame bl C_FUNC(RhpWaitForGC2) POP_PROBE_FRAME diff --git a/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosloongarch64.inc b/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosloongarch64.inc index 265a188f82eb4a..cf3583aae5ba87 100644 --- a/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosloongarch64.inc +++ b/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosloongarch64.inc @@ -198,6 +198,7 @@ C_FUNC(\Name): #define PTFF_SAVE_SP 0x00000200 #define PTFF_SAVE_R4 0x00000800 #define PTFF_SAVE_R5 0x00001000 +#define PTFF_SAVE_R6 0x00002000 #define PTFF_SAVE_ALL_PRESERVED 0x000001FF // NOTE: r23-r31 #define PTFF_THREAD_HIJACK 0x80000000 diff --git a/src/coreclr/pal/src/CMakeLists.txt b/src/coreclr/pal/src/CMakeLists.txt index 4688e883263d54..6903d78e15c9de 100644 --- a/src/coreclr/pal/src/CMakeLists.txt +++ b/src/coreclr/pal/src/CMakeLists.txt @@ -86,9 +86,9 @@ endif(CLR_CMAKE_TARGET_APPLE) if (FEATURE_ENABLE_NO_ADDRESS_SPACE_RANDOMIZATION) add_definitions(-DFEATURE_ENABLE_NO_ADDRESS_SPACE_RANDOMIZATION) endif(FEATURE_ENABLE_NO_ADDRESS_SPACE_RANDOMIZATION) -if(FEATURE_SINGLE_THREADED) - add_definitions(-DFEATURE_SINGLE_THREADED) -endif(FEATURE_SINGLE_THREADED) +if(NOT CLR_CMAKE_TARGET_ARCH_WASM OR FEATURE_MULTITHREADING) + add_definitions(-DFEATURE_MULTITHREADING) +endif(NOT CLR_CMAKE_TARGET_ARCH_WASM OR FEATURE_MULTITHREADING) add_definitions(-DLP64COMPATIBLE) add_definitions(-DCORECLR) add_definitions(-DPIC) diff --git a/src/coreclr/pal/src/synchmgr/synchmanager.cpp b/src/coreclr/pal/src/synchmgr/synchmanager.cpp index db86006fa33680..e972697e0281e6 100644 --- a/src/coreclr/pal/src/synchmgr/synchmanager.cpp +++ b/src/coreclr/pal/src/synchmgr/synchmanager.cpp @@ -404,7 +404,7 @@ namespace CorUnix goto TNW_exit; } -#ifdef FEATURE_SINGLE_THREADED +#ifndef FEATURE_MULTITHREADING // In single-threaded WASM, if the object is not already signaled (iPred == FALSE), // we cannot wait because there is no other thread to signal us - this would deadlock. // This is a programming error in single-threaded WASM. @@ -415,7 +415,7 @@ namespace CorUnix palErr = ERROR_NOT_SUPPORTED; *ptwrWakeupReason = WaitFailed; } -#else // FEATURE_SINGLE_THREADED +#else // !FEATURE_MULTITHREADING while (FALSE == ptnwdNativeWaitData->iPred) { @@ -488,7 +488,7 @@ namespace CorUnix *ptwrWakeupReason = WaitTimeout; } -#endif // FEATURE_SINGLE_THREADED +#endif // !FEATURE_MULTITHREADING TNW_exit: TRACE("ThreadNativeWait: returning %u [WakeupReason=%u]\n", palErr, *ptwrWakeupReason); return palErr; diff --git a/src/coreclr/pal/src/synchmgr/wait.cpp b/src/coreclr/pal/src/synchmgr/wait.cpp index 37ab3f9bd55928..7efa82c01a8c4f 100644 --- a/src/coreclr/pal/src/synchmgr/wait.cpp +++ b/src/coreclr/pal/src/synchmgr/wait.cpp @@ -418,14 +418,14 @@ DWORD CorUnix::InternalWaitForMultipleObjectsEx( if (fNeedToBlock) { -#ifdef FEATURE_SINGLE_THREADED +#ifndef FEATURE_MULTITHREADING // In single-threaded WASM, blocking would deadlock because there is no other // thread to signal the object. This is a programming error. _ASSERT_MSG(false, "Cannot block on wait in single-threaded mode\n"); pThread->SetLastError(ERROR_NOT_SUPPORTED); dwRet = WAIT_FAILED; goto WFMOExIntCleanup; -#else // FEATURE_SINGLE_THREADED +#else // !FEATURE_MULTITHREADING ThreadWakeupReason twrWakeupReason; // @@ -460,7 +460,7 @@ DWORD CorUnix::InternalWaitForMultipleObjectsEx( dwRet = WAIT_FAILED; break; } -#endif // FEATURE_SINGLE_THREADED +#endif // !FEATURE_MULTITHREADING } if (!fWAll && (WAIT_OBJECT_0 == dwRet)) @@ -502,13 +502,13 @@ DWORD CorUnix::InternalSleepEx ( CPalThread * pThread, DWORD dwMilliseconds) { -#ifdef FEATURE_SINGLE_THREADED +#ifndef FEATURE_MULTITHREADING // In single-threaded WASM, Sleep returns immediately. // There's no other thread that could make progress while we sleep. (void)pThread; (void)dwMilliseconds; return 0; -#else // FEATURE_SINGLE_THREADED +#else // !FEATURE_MULTITHREADING PAL_ERROR palErr = NO_ERROR; DWORD dwRet = WAIT_FAILED; int iSignaledObjIndex; @@ -551,6 +551,6 @@ DWORD CorUnix::InternalSleepEx ( TRACE("Done sleeping %u ms", dwMilliseconds); return dwRet; -#endif // FEATURE_SINGLE_THREADED +#endif // !FEATURE_MULTITHREADING } diff --git a/src/coreclr/pal/src/thread/thread.cpp b/src/coreclr/pal/src/thread/thread.cpp index bba63b6373ece5..bd3ec6137ed5f6 100644 --- a/src/coreclr/pal/src/thread/thread.cpp +++ b/src/coreclr/pal/src/thread/thread.cpp @@ -434,10 +434,10 @@ CorUnix::InternalCreateThread( HANDLE *phThread ) { -#ifdef FEATURE_SINGLE_THREADED +#ifndef FEATURE_MULTITHREADING ERROR("Threads are not supported in single-threaded mode.\n"); return ERROR_NOT_SUPPORTED; -#else // FEATURE_SINGLE_THREADED +#else // !FEATURE_MULTITHREADING PAL_ERROR palError; CPalThread *pNewThread = NULL; CObjectAttributes oa; @@ -633,7 +633,7 @@ CorUnix::InternalCreateThread( } return palError; -#endif // FEATURE_SINGLE_THREADED +#endif // !FEATURE_MULTITHREADING } @@ -1357,7 +1357,7 @@ SetThreadDescription( return HRESULT_FROM_WIN32(palError); } -#ifndef FEATURE_SINGLE_THREADED +#ifdef FEATURE_MULTITHREADING void * CPalThread::ThreadEntry( void *pvParam @@ -1514,7 +1514,7 @@ CPalThread::ThreadEntry( above should release all resources */ return NULL; } -#endif // !FEATURE_SINGLE_THREADED +#endif // FEATURE_MULTITHREADING /*++ Function: diff --git a/src/coreclr/pal/src/thread/threadsusp.cpp b/src/coreclr/pal/src/thread/threadsusp.cpp index 81d9896961ec3a..92f23f9537c47a 100644 --- a/src/coreclr/pal/src/thread/threadsusp.cpp +++ b/src/coreclr/pal/src/thread/threadsusp.cpp @@ -44,7 +44,7 @@ SET_DEFAULT_DEBUG_CHANNEL(THREAD); in suspended state in order to resume it. */ CONST BYTE WAKEUPCODE=0x2A; -#ifndef FEATURE_SINGLE_THREADED +#ifdef FEATURE_MULTITHREADING /*++ Function: InternalSuspendNewThreadFromData @@ -119,7 +119,7 @@ CThreadSuspensionInfo::InternalSuspendNewThreadFromData( return palError; } -#endif // !FEATURE_SINGLE_THREADED +#endif // FEATURE_MULTITHREADING /*++ Function: @@ -134,10 +134,10 @@ ResumeThread( IN HANDLE hThread ) { -#ifdef FEATURE_SINGLE_THREADED +#ifndef FEATURE_MULTITHREADING ERROR("Threads are not supported in single-threaded mode.\n"); return ERROR_NOT_SUPPORTED; -#else // FEATURE_SINGLE_THREADED +#else // !FEATURE_MULTITHREADING PAL_ERROR palError; CPalThread *pthrResumer; DWORD dwSuspendCount = (DWORD)-1; @@ -165,7 +165,7 @@ ResumeThread( LOGEXIT("ResumeThread returns DWORD %u\n", dwSuspendCount); PERF_EXIT(ResumeThread); return dwSuspendCount; -#endif // FEATURE_SINGLE_THREADED +#endif // !FEATURE_MULTITHREADING } /*++ diff --git a/src/coreclr/pal/tests/palsuite/miscellaneous/CloseHandle/test1/test.cpp b/src/coreclr/pal/tests/palsuite/miscellaneous/CloseHandle/test1/test.cpp index a45322ad2ef45e..304ec987184889 100644 --- a/src/coreclr/pal/tests/palsuite/miscellaneous/CloseHandle/test1/test.cpp +++ b/src/coreclr/pal/tests/palsuite/miscellaneous/CloseHandle/test1/test.cpp @@ -10,7 +10,7 @@ ** **=========================================================*/ -/* Depends on: CreateFile and WriteFile */ +/* Depends on: CreateFile */ #include @@ -18,7 +18,6 @@ PALTEST(miscellaneous_CloseHandle_test1_paltest_closehandle_test1, "miscellaneou { HANDLE FileHandle = NULL; - LPDWORD WriteBuffer; /* Used with WriteFile */ /* * Initialize the PAL and return FAILURE if this fails @@ -29,35 +28,20 @@ PALTEST(miscellaneous_CloseHandle_test1_paltest_closehandle_test1, "miscellaneou return FAIL; } - WriteBuffer = (LPDWORD)malloc(sizeof(WORD)); - - if ( WriteBuffer == NULL ) - { - Fail("ERROR: Failed to allocate memory for WriteBuffer pointer. " - "Can't properly exec test case without this.\n"); - } - - /* Create a file, since this returns to us a HANDLE we can use */ - FileHandle = CreateFile("testfile", - GENERIC_READ | GENERIC_WRITE,0,NULL,CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL, + FileHandle = CreateFile("testfile", + GENERIC_READ | GENERIC_WRITE,0,NULL,CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, NULL); - + /* Should be able to close this handle */ if(CloseHandle(FileHandle) == 0) { - free(WriteBuffer); Fail("ERROR: (Test 1) Attempted to close a HANDLE on a file, but the " - "return value was <=0, indicating failure.\n"); + "return value was <=0, indicating failure.\n"); } - - free(WriteBuffer); - + PAL_Terminate(); return PASS; } - - - diff --git a/src/coreclr/scripts/superpmi.py b/src/coreclr/scripts/superpmi.py index 057a130765a2b3..6c9740406ef036 100644 --- a/src/coreclr/scripts/superpmi.py +++ b/src/coreclr/scripts/superpmi.py @@ -20,6 +20,7 @@ import asyncio import csv import datetime +import html import json import locale import logging @@ -2997,7 +2998,7 @@ def write_example_diffs_to_markdown_summary(write_fh, asm_diffs): for (func_name, diff, diff_text) in examples_to_put_in_summary: base_size = int(diff["Base ActualCodeBytes"]) diff_size = int(diff["Diff ActualCodeBytes"]) - with DetailsSection(write_fh, "{} ({}) : {}".format(format_delta(base_size, diff_size), compute_and_format_pct(base_size, diff_size), func_name)): + with DetailsSection(write_fh, "{} ({}) : {}".format(format_delta(base_size, diff_size), compute_and_format_pct(base_size, diff_size), html.escape(func_name))): write_fh.write(diff_text) ################################################################################ diff --git a/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.cs b/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.cs index db8ad465dc2fe3..77726678807362 100644 --- a/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.cs +++ b/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.cs @@ -17,6 +17,7 @@ namespace ILCompiler public partial class CompilerTypeSystemContext : MetadataTypeSystemContext, IMetadataStringDecoderProvider { private readonly MetadataRuntimeInterfacesAlgorithm _metadataRuntimeInterfacesAlgorithm = new MetadataRuntimeInterfacesAlgorithm(); + private readonly AsyncAwareVirtualMethodResolutionAlgorithm _virtualMethodAlgorithm; private MetadataStringDecoder _metadataStringDecoder; diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/AssemblyStubNode.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/AssemblyStubNode.cs index e31f93bb39bb47..9fdb87be78def2 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/AssemblyStubNode.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/AssemblyStubNode.cs @@ -7,6 +7,9 @@ namespace ILCompiler.DependencyAnalysis { + // TODO-Wasm: Some instances of AssemblyStubNode will need to implement INodeWithTypeSignature + // if they need to be callable from Wasm, though it may not make sense for the base + // class to implement INodeWithTypeSignature. public abstract class AssemblyStubNode : ObjectNode, ISymbolDefinitionNode { public AssemblyStubNode() diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/INodeWithTypeSignature.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/INodeWithTypeSignature.cs new file mode 100644 index 00000000000000..0471d3ca296a63 --- /dev/null +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/INodeWithTypeSignature.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + public interface INodeWithTypeSignature : ISymbolDefinitionNode + { + MethodSignature Signature { get; } + bool IsUnmanagedCallersOnly { get; } + } + + public interface IMethodCodeNodeWithTypeSignature : IMethodNode, INodeWithTypeSignature + { + MethodSignature INodeWithTypeSignature.Signature => Method.Signature; + bool INodeWithTypeSignature.IsUnmanagedCallersOnly => Method.IsUnmanagedCallersOnly; + } +} diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectNode.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectNode.cs index 327447571686fa..151fa1e8f587bf 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectNode.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectNode.cs @@ -68,6 +68,14 @@ public sealed override IEnumerable GetStaticDependencies(No } } + if (factory.Target.IsWasm && this is IMethodCodeNodeWithTypeSignature wasmMethodCodeNode) + { + dependencies ??= new DependencyList(); + + WasmTypeNode wasmTypeNode = factory.WasmTypeNode(wasmMethodCodeNode.Method); + dependencies.Add(wasmTypeNode, "Wasm Method Code Nodes Require Signature"); + } + if (dependencies == null) return Array.Empty(); else diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs index fa68031839b1d4..d8f541b8f88d03 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs @@ -449,6 +449,7 @@ private static unsafe void PutLoongArch64PC12(uint* pCode, long imm) Debug.Assert((pcInstr & 0xFE000000) == 0x1a000000); // Must be pcalau12i + pcInstr &= 0xFE00001F; // keep bits 31-25, 4-0 // Assemble the pc-relative high 20 bits of 'imm' into the pcalau12i instruction pcInstr |= (uint)((imm >> 7) & 0x1FFFFE0); @@ -456,7 +457,8 @@ private static unsafe void PutLoongArch64PC12(uint* pCode, long imm) pcInstr = *(pCode + 1); - // Assemble the pc-relative low 12 bits of 'imm' into the addid or ld instruction + pcInstr &= 0xFFC003FF; // keep bits 31-22, 9-0 + // Assemble the pc-relative low 12 bits of 'imm' into the addi.d or ld instruction pcInstr |= (uint)((imm & 0xFFF) << 10); *(pCode + 1) = pcInstr; // write the assembled instruction @@ -493,6 +495,7 @@ private static unsafe void PutLoongArch64JIR(uint* pCode, long imm38) long imm = imm38 + relOff; relOff = (((imm & 0x1ffff) - relOff) >> 2) & 0xffff; + pcInstr &= 0xFE00001F; // keep bits 31-25, 4-0 // Assemble the pc-relative high 20 bits of 'imm38' into the pcaddu18i instruction pcInstr |= (uint)(((imm >> 18) & 0xFFFFF) << 5); @@ -500,6 +503,7 @@ private static unsafe void PutLoongArch64JIR(uint* pCode, long imm38) pcInstr = *(pCode + 1); + pcInstr &= 0xFC0003FF; // keep bits 31-26, 9-0 // Assemble the pc-relative low 18 bits of 'imm38' into the jirl instruction pcInstr |= (uint)(relOff << 10); diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_Wasm/WasmTypes.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_Wasm/WasmTypes.cs index da37129e4b501e..f226eed7b825fe 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_Wasm/WasmTypes.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_Wasm/WasmTypes.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics; using System.Linq; + using ILCompiler.ObjectWriter; using Internal.JitInterface; diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_Wasm/WasmTypeNode.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/WasmTypeNode.cs similarity index 96% rename from src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_Wasm/WasmTypeNode.cs rename to src/coreclr/tools/Common/Compiler/DependencyAnalysis/WasmTypeNode.cs index 4ef0659e166c1a..20deff75fe2e83 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_Wasm/WasmTypeNode.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/WasmTypeNode.cs @@ -2,10 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using ILCompiler.DependencyAnalysis; + using ILCompiler.ObjectWriter; +using ILCompiler.DependencyAnalysis.Wasm; -namespace ILCompiler.DependencyAnalysis.Wasm +namespace ILCompiler.DependencyAnalysis { // // Represents a WASM type signature, e.g. "(i32, i32) -> (i64)". Used as a relocation target for things like 'call_indirect'. diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/ObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/ObjectWriter.cs index 9f0f397a364a07..a4eca181141b60 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/ObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/ObjectWriter.cs @@ -12,11 +12,12 @@ using ILCompiler.DependencyAnalysisFramework; using Internal.Text; using Internal.TypeSystem; + using static ILCompiler.DependencyAnalysis.ObjectNode; using static ILCompiler.DependencyAnalysis.RelocType; using ObjectData = ILCompiler.DependencyAnalysis.ObjectNode.ObjectData; + using CodeDataLayout = CodeDataLayoutMode.CodeDataLayout; -using ILCompiler.DependencyAnalysis.Wasm; namespace ILCompiler.ObjectWriter { @@ -441,17 +442,27 @@ public virtual void EmitObject(Stream outputFileStream, IReadOnlyCollection _uniqueSignatures = new(); private Dictionary _uniqueSymbols = new(); - private int _signatureCount = 0; private int _methodCount = 0; private protected override void RecordMethodSignature(WasmTypeNode signature) { - int signatureIndex = _signatureCount; var mangledNameBuilder = new Utf8StringBuilder(); signature.AppendMangledName(_nodeFactory.NameMangler, mangledNameBuilder); Utf8String mangledName = mangledNameBuilder.ToUtf8String(); - // Note that we do not expect duplicates here, since crossgen's node cache should handle this and all nodes representing - // identical signatures in a module should point to the same node instance - _uniqueSignatures.Add(mangledName, signatureIndex); - _signatureCount++; + // Note that we do not expect duplicates here, crossgen should deduplicate signatures already + // using the node cache, so we can simply add the new signature with the next available index. + _uniqueSignatures.Add(mangledName, _uniqueSignatures.Count); } - private protected override void RecordMethodDeclaration(ISymbolDefinitionNode symbol, MethodDesc desc) + private protected override void RecordMethodDeclaration(INodeWithTypeSignature node, MethodDesc desc) { WriteSignatureIndexForFunction(desc); - _uniqueSymbols.Add(symbol.GetMangledName(_nodeFactory.NameMangler), _methodCount); + _uniqueSymbols.Add(node.GetMangledName(_nodeFactory.NameMangler), _methodCount); _methodCount++; } diff --git a/src/coreclr/tools/Common/JitInterface/WasmLowering.cs b/src/coreclr/tools/Common/JitInterface/WasmLowering.cs index 0db56ce99bd138..570f2d03c731f3 100644 --- a/src/coreclr/tools/Common/JitInterface/WasmLowering.cs +++ b/src/coreclr/tools/Common/JitInterface/WasmLowering.cs @@ -4,7 +4,9 @@ using System; using System.Collections.Generic; using System.Diagnostics; + using ILCompiler.DependencyAnalysis.Wasm; + using Internal.TypeSystem; namespace Internal.JitInterface diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.Aot.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.Aot.cs index e112e4ca14cb97..cc3a45e3c9b334 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.Aot.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.Aot.cs @@ -39,8 +39,6 @@ public SharedGenericsConfiguration GenericsConfig private readonly Int128FieldLayoutAlgorithm _int128FieldLayoutAlgorithm; private readonly TypeWithRepeatedFieldsFieldLayoutAlgorithm _typeWithRepeatedFieldsFieldLayoutAlgorithm; - private readonly AsyncAwareVirtualMethodResolutionAlgorithm _virtualMethodAlgorithm; - private TypeDesc[] _arrayOfTInterfaces; private TypeDesc[] _arrayEnumeratorOfTInterfaces; private ArrayOfTRuntimeInterfacesAlgorithm _arrayOfTRuntimeInterfacesAlgorithm; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs index 820cf40f5813be..44f752a62fc035 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.Text; +using ILCompiler.DependencyAnalysis.Wasm; using ILCompiler.DependencyAnalysisFramework; using Internal.IL; @@ -613,6 +614,11 @@ private void CreateNodeCaches() return new AnalysisCharacteristicNode(c); }); + _wasmTypeNodes = new NodeCache(key => + { + return new WasmTypeNode(key); + }); + NativeLayout = new NativeLayoutHelper(this); } @@ -1569,6 +1575,17 @@ public AnalysisCharacteristicNode AnalysisCharacteristic(string ch) return _analysisCharacteristics.GetOrAdd(ch); } + private NodeCache _wasmTypeNodes; + + // TODO-Wasm: Do not use WasmFuncType directly as the key for better + // memory efficiency on lookup + public WasmTypeNode WasmTypeNode(MethodDesc desc) + { + // TODO-Wasm: Construct proper function type based on the passed in MethodDesc + // once we have defined lowering rules for signatures in NativeAOT. + throw new NotImplementedException("NAOT wasm type signature lowering not yet implemented"); + } + /// /// Returns alternative symbol name that object writer should produce for given symbols /// in addition to the regular one. diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index fecf02a45a3c46..c4061f8c4af147 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -309,6 +309,8 @@ + + @@ -325,7 +327,6 @@ - diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelayLoadHelperMethodImport.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelayLoadHelperMethodImport.cs index 4c73a0ab08bcab..980dfc535ceb88 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelayLoadHelperMethodImport.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelayLoadHelperMethodImport.cs @@ -15,7 +15,7 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun /// In addition to PrecodeHelperImport instances of this import type emit GC ref map /// entries into the R2R executable. /// - public class DelayLoadHelperMethodImport : DelayLoadHelperImport, IMethodNode + public class DelayLoadHelperMethodImport : DelayLoadHelperImport, IMethodCodeNodeWithTypeSignature { private readonly MethodWithToken _method; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelayLoadMethodImport.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelayLoadMethodImport.cs index dd0d49c8db19c2..3bdef8fb2f8be4 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelayLoadMethodImport.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DelayLoadMethodImport.cs @@ -9,7 +9,7 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun { - public class DelayLoadMethodImport : DelayLoadHelperImport, IMethodNode + public class DelayLoadMethodImport : DelayLoadHelperImport, IMethodCodeNodeWithTypeSignature { private readonly MethodWithGCInfo _localMethod; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodWithGCInfo.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodWithGCInfo.cs index aa3b9efa2f1526..0f65826a2c2d0e 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodWithGCInfo.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodWithGCInfo.cs @@ -13,7 +13,7 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun { - public class MethodWithGCInfo : ObjectNode, IMethodBodyNode, ISymbolDefinitionNode + public class MethodWithGCInfo : ObjectNode, IMethodBodyNode, IMethodCodeNodeWithTypeSignature { public readonly MethodGCInfoNode GCInfoNode; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_Wasm/ImportThunk.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_Wasm/ImportThunk.cs index e84b9e8a4f381e..1f606488623bfa 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_Wasm/ImportThunk.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_Wasm/ImportThunk.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; + using ILCompiler.DependencyAnalysis.Wasm; namespace ILCompiler.DependencyAnalysis.ReadyToRun diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs index b8fe7c2466f5f6..8a9602d24f51ef 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs @@ -1096,5 +1096,13 @@ public WasmTypeNode WasmTypeNode(CorInfoWasmType[] types) WasmFuncType funcType = WasmFuncType.FromCorInfoSignature(types); return _wasmTypeNodes.GetOrAdd(funcType); } + + // TODO-Wasm: Do not use WasmFuncType directly as the key for better + // memory efficiency on lookup + public WasmTypeNode WasmTypeNode(MethodDesc method) + { + WasmFuncType funcType = WasmLowering.GetSignature(method); + return _wasmTypeNodes.GetOrAdd(funcType); + } } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs index cd6bc5040fd10e..0a882fadd65ffa 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs @@ -11,11 +11,10 @@ namespace ILCompiler { partial class CompilerTypeSystemContext { - private readonly MetadataVirtualMethodAlgorithm _virtualMethodAlgorithm = new MetadataVirtualMethodAlgorithm(); - public CompilerTypeSystemContext(TargetDetails details, SharedGenericsMode genericsMode) : base(details) { + _virtualMethodAlgorithm = new AsyncAwareVirtualMethodResolutionAlgorithm(this); _continuationTypeHashtable = new(this); _genericsMode = genericsMode; } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj index 1d78e70b1f47a4..e57807fa2c0c1c 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj @@ -83,6 +83,7 @@ + @@ -96,6 +97,7 @@ + @@ -113,7 +115,6 @@ - diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index 3b9259043aec10..59f6d750c9e74a 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -24,6 +24,7 @@ using ILCompiler.DependencyAnalysis; using ILCompiler.DependencyAnalysis.ReadyToRun; using ILCompiler.DependencyAnalysis.Wasm; + using System.Text; using System.Runtime.CompilerServices; using ILCompiler.ReadyToRun.TypeSystem; @@ -1484,6 +1485,8 @@ ModuleToken GetModuleTokenForType(TypeSystemEntity resultDef) token = (mdToken)MetadataTokens.GetToken(ecmaType.Handle); module = ecmaType.Module; return new ModuleToken(module, token); + case TypeDesc typeDesc: + return _compilation.NodeFactory.Resolver.GetModuleTokenForType(typeDesc, allowDynamicallyCreatedReference: true, throwIfNotFound: true); default: throw new NotImplementedException($"Unsupported token resolution for {resultDef.GetType()}"); } diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/DependencyAnalysis/MethodCodeNode.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/DependencyAnalysis/MethodCodeNode.cs index 7681e8c062dc27..64920d89a0b951 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/DependencyAnalysis/MethodCodeNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/DependencyAnalysis/MethodCodeNode.cs @@ -15,7 +15,7 @@ namespace ILCompiler.DependencyAnalysis { [DebuggerTypeProxy(typeof(MethodCodeNodeDebugView))] - public class MethodCodeNode : ObjectNode, IMethodBodyNode, INodeWithCodeInfo, INodeWithDebugInfo, ISymbolDefinitionNode, ISpecialUnboxThunkNode + public class MethodCodeNode : ObjectNode, IMethodBodyNode, INodeWithCodeInfo, INodeWithDebugInfo, ISpecialUnboxThunkNode, IMethodCodeNodeWithTypeSignature { private MethodDesc _method; private ObjectData _methodCode; diff --git a/src/coreclr/utilcode/util.cpp b/src/coreclr/utilcode/util.cpp index 4efc5bd767c77c..b85a788096c56b 100644 --- a/src/coreclr/utilcode/util.cpp +++ b/src/coreclr/utilcode/util.cpp @@ -2185,6 +2185,7 @@ void PutLoongArch64PC12(UINT32 * pCode, INT64 imm) _ASSERTE((pcInstr & 0xFE000000) == 0x1a000000); // Must be pcalau12i + pcInstr &= 0xFE00001F; // keep bits 31-25, 4-0 // Assemble the pc-relative high 20 bits of 'imm' into the pcalau12i instruction pcInstr |= (UINT32)((imm >> 7) & 0x1FFFFE0); @@ -2192,6 +2193,7 @@ void PutLoongArch64PC12(UINT32 * pCode, INT64 imm) pcInstr = *(pCode + 1); + pcInstr &= 0xFFC003FF; // keep bits 31-22, 9-0 // Assemble the pc-relative low 12 bits of 'imm' into the addid or ld instruction pcInstr |= (UINT32)((imm & 0xFFF) << 10); @@ -2218,6 +2220,7 @@ void PutLoongArch64JIR(UINT32 * pCode, INT64 imm38) INT64 imm = imm38 + relOff; relOff = (((imm & 0x1ffff) - relOff) >> 2) & 0xffff; + pcInstr &= 0xFE00001F; // keep bits 31-25, 4-0 // Assemble the pc-relative high 20 bits of 'imm38' into the pcaddu18i instruction pcInstr |= (UINT32)(((imm >> 18) & 0xFFFFF) << 5); @@ -2225,6 +2228,7 @@ void PutLoongArch64JIR(UINT32 * pCode, INT64 imm38) pcInstr = *(pCode + 1); + pcInstr &= 0xFC0003FF; // keep bits 31-26, 9-0 // Assemble the pc-relative low 18 bits of 'imm38' into the jirl instruction pcInstr |= (UINT32)(relOff << 10); diff --git a/src/coreclr/vm/appdomain.cpp b/src/coreclr/vm/appdomain.cpp index 88d6b1642b0f24..4b519cb48aa8e9 100644 --- a/src/coreclr/vm/appdomain.cpp +++ b/src/coreclr/vm/appdomain.cpp @@ -2537,15 +2537,15 @@ Assembly *AppDomain::LoadAssembly(FileLoadLock *pLock, FileLoadLevel targetLevel } CONTRACT_END; - Assembly *pAssembly = pLock->GetAssembly(); - // Make sure we release the lock on exit FileLoadLockRefHolder lockRef(pLock); // Do a quick out check for the already loaded case. if (pLock->GetLoadLevel() >= targetLevel) { + Assembly* pAssembly = pLock->GetAssembly(); _ASSERTE(pAssembly != nullptr); + pAssembly->ThrowIfError(targetLevel); RETURN pAssembly; @@ -2616,7 +2616,7 @@ Assembly *AppDomain::LoadAssembly(FileLoadLock *pLock, FileLoadLevel targetLevel fileLoadLevelName[pLock->GetLoadLevel()])); } - pAssembly = pLock->GetAssembly(); + Assembly* pAssembly = pLock->GetAssembly(); _ASSERTE(pAssembly != nullptr); // We should always be loading to at least FILE_LOAD_ALLOCATE, so the assembly should be created // There may have been an error stored on the domain file by another thread, or from a previous load diff --git a/src/coreclr/vm/callstubgenerator.cpp b/src/coreclr/vm/callstubgenerator.cpp index eda86fa5b7b47f..ce30ce8757cc8f 100644 --- a/src/coreclr/vm/callstubgenerator.cpp +++ b/src/coreclr/vm/callstubgenerator.cpp @@ -1687,7 +1687,7 @@ void InitCallStubGenerator() s_callStubCache = new CallStubCacheHash; } -CallStubHeader *CallStubGenerator::GenerateCallStubForSig(MetaSig &sig) +CallStubHeader *CallStubGenerator::GenerateCallStubForSig(MetaSig &sig, MethodDesc *pContextMD) { STANDARD_VM_CONTRACT; @@ -1699,7 +1699,7 @@ CallStubHeader *CallStubGenerator::GenerateCallStubForSig(MetaSig &sig) m_interpreterToNative = true; // We always generate the interpreter to native call stub here - ComputeCallStub(sig, pRoutines, NULL); + ComputeCallStub(sig, pRoutines, pContextMD); xxHash hashState; for (int i = 0; i < m_routineIndex; i++) diff --git a/src/coreclr/vm/callstubgenerator.h b/src/coreclr/vm/callstubgenerator.h index 3bf5b93fa9b98c..c2996de338fba3 100644 --- a/src/coreclr/vm/callstubgenerator.h +++ b/src/coreclr/vm/callstubgenerator.h @@ -237,7 +237,7 @@ class CallStubGenerator public: // Generate the call stub for the given method. CallStubHeader *GenerateCallStub(MethodDesc *pMD, AllocMemTracker *pamTracker, bool interpreterToNative); - CallStubHeader *GenerateCallStubForSig(MetaSig &sig); + CallStubHeader *GenerateCallStubForSig(MetaSig &sig, MethodDesc *pContextMD = nullptr); private: static size_t ComputeTempStorageSize(const MetaSig& sig) diff --git a/src/coreclr/vm/clrtocomcall.cpp b/src/coreclr/vm/clrtocomcall.cpp index 70cd6c13ff74ff..3a2373b8e8735b 100644 --- a/src/coreclr/vm/clrtocomcall.cpp +++ b/src/coreclr/vm/clrtocomcall.cpp @@ -316,16 +316,10 @@ UINT32 CLRToCOMEventCallWorker(CLRToCOMMethodFrame* pFrame, CLRToCOMCallMethodDe gc.EventProviderTypeObj = pEvProvMT->GetManagedClassObject(); gc.ThisObj = pFrame->GetThis(); - MethodDescCallSite getEventProvider(METHOD__COM_OBJECT__GET_EVENT_PROVIDER, &gc.ThisObj); + UnmanagedCallersOnlyCaller getEventProvider(METHOD__COM_OBJECT__GET_EVENT_PROVIDER); // Retrieve the event provider for the event interface type. - ARG_SLOT GetEventProviderArgs[] = - { - ObjToArgSlot(gc.ThisObj), - ObjToArgSlot(gc.EventProviderTypeObj) - }; - - gc.EventProviderObj = getEventProvider.Call_RetOBJECTREF(GetEventProviderArgs); + getEventProvider.InvokeThrowing(&gc.ThisObj, &gc.EventProviderTypeObj, &gc.EventProviderObj); // Set up an arg iterator to retrieve the arguments from the frame. MetaSig mSig(pMD); @@ -601,6 +595,7 @@ UINT32 CLRToCOMLateBoundWorker( { OBJECTREF MemberName; OBJECTREF ItfTypeObj; + OBJECTREF ThisObj; PTRARRAYREF Args; BOOLARRAYREF ArgsIsByRef; PTRARRAYREF ArgsTypes; @@ -610,6 +605,7 @@ UINT32 CLRToCOMLateBoundWorker( } gc; gc.MemberName = NULL; gc.ItfTypeObj = NULL; + gc.ThisObj = NULL; gc.Args = NULL; gc.ArgsIsByRef = NULL; gc.ArgsTypes = NULL; @@ -648,6 +644,7 @@ UINT32 CLRToCOMLateBoundWorker( // Return type TypeHandle retValHandle = callsite.MetaSig.GetRetTypeHandleThrowing(); gc.RetValType = retValHandle.GetManagedClassObject(); + gc.ThisObj = pFrame->GetThis(); // the return value is written into the Frame's neginfo, so we don't // need to return it directly. We can just have the stub do that work. @@ -662,24 +659,20 @@ UINT32 CLRToCOMLateBoundWorker( } // Create a call site for the invoke - MethodDescCallSite forwardCallToInvoke(METHOD__CLASS__FORWARD_CALL_TO_INVOKE, &gc.ItfTypeObj); - - // Prepare the arguments that will be passed to the method. - ARG_SLOT invokeArgs[] = - { - ObjToArgSlot(gc.ItfTypeObj), - ObjToArgSlot(gc.MemberName), - (ARG_SLOT)binderFlags, - ObjToArgSlot(pFrame->GetThis()), - ObjToArgSlot(gc.Args), - ObjToArgSlot(gc.ArgsIsByRef), - ObjToArgSlot(gc.ArgsWrapperTypes), - ObjToArgSlot(gc.ArgsTypes), - ObjToArgSlot(gc.RetValType) - }; + UnmanagedCallersOnlyCaller forwardCallToInvoke(METHOD__CLASS__FORWARD_CALL_TO_INVOKE); // Invoke the method - gc.RetVal = forwardCallToInvoke.CallWithValueTypes_RetOBJECTREF(invokeArgs); + forwardCallToInvoke.InvokeThrowing( + &gc.ItfTypeObj, + &gc.MemberName, + (INT32)binderFlags, + &gc.ThisObj, + &gc.Args, + &gc.ArgsIsByRef, + &gc.ArgsWrapperTypes, + &gc.ArgsTypes, + &gc.RetValType, + &gc.RetVal); // Ensure all outs and return values are moved back to the current callsite CallsiteInspect::PropagateOutParametersBackToCallsite(gc.Args, gc.RetVal, callsite); diff --git a/src/coreclr/vm/comcache.h b/src/coreclr/vm/comcache.h index 61059b9c9cec4b..2f02827f3879a2 100644 --- a/src/coreclr/vm/comcache.h +++ b/src/coreclr/vm/comcache.h @@ -262,6 +262,15 @@ struct InterfaceEntry // will not try and optimize reads and writes to them. Volatile m_pMT; // Interface asked for Volatile m_pUnknown; // Result of query + + friend struct ::cdac_data; +}; + +template<> +struct cdac_data +{ + static constexpr size_t MethodTable = offsetof(InterfaceEntry, m_pMT); + static constexpr size_t Unknown = offsetof(InterfaceEntry, m_pUnknown); }; class CtxEntryCacheTraits : public DefaultSHashTraits diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index 0199ff57ae6d07..03655015fe1e50 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -159,7 +159,7 @@ DEFINE_METHOD(CLASS, CTOR, .ctor, #endif // FOR_ILLINK #ifdef FEATURE_COMINTEROP -DEFINE_METHOD(CLASS, FORWARD_CALL_TO_INVOKE, ForwardCallToInvokeMember, IM_Str_BindingFlags_Obj_ArrObj_ArrBool_ArrInt_ArrType_Type_RetObj) +DEFINE_METHOD(CLASS, FORWARD_CALL_TO_INVOKE, ForwardCallToInvokeMember, SM_PtrClass_PtrStr_Int_PtrObj_PtrArrObj_PtrArrBool_PtrArrInt_PtrArrType_PtrType_PtrObj_PtrException_RetVoid) #endif // FEATURE_COMINTEROP BEGIN_ILLINK_FEATURE_SWITCH(System.Runtime.InteropServices.BuiltInComInterop.IsSupported, true, true) @@ -179,15 +179,15 @@ DEFINE_CLASS_U(System, __ComObject, ComObject) DEFINE_FIELD_U(m_ObjectToDataMap, ComObject, m_ObjectToDataMap) DEFINE_CLASS(COM_OBJECT, System, __ComObject) DEFINE_METHOD(COM_OBJECT, RELEASE_ALL_DATA, ReleaseAllData, IM_RetVoid) -DEFINE_METHOD(COM_OBJECT, GET_EVENT_PROVIDER, GetEventProvider, IM_Class_RetObj) +DEFINE_METHOD(COM_OBJECT, GET_EVENT_PROVIDER, GetEventProvider, SM_PtrComObject_PtrClass_PtrObj_PtrException_RetVoid) #ifdef FOR_ILLINK DEFINE_METHOD(COM_OBJECT, CTOR, .ctor, IM_RetVoid) #endif // FOR_ILLINK DEFINE_CLASS(LICENSE_INTEROP_PROXY, InternalInteropServices, LicenseInteropProxy) -DEFINE_METHOD(LICENSE_INTEROP_PROXY, CREATE, Create, SM_RetObj) -DEFINE_METHOD(LICENSE_INTEROP_PROXY, GETCURRENTCONTEXTINFO, GetCurrentContextInfo, IM_RuntimeTypeHandle_RefBool_RefIntPtr_RetVoid) -DEFINE_METHOD(LICENSE_INTEROP_PROXY, SAVEKEYINCURRENTCONTEXT, SaveKeyInCurrentContext, IM_IntPtr_RetVoid) +DEFINE_METHOD(LICENSE_INTEROP_PROXY, CREATE, Create, SM_PtrObj_PtrException_RetVoid) +DEFINE_METHOD(LICENSE_INTEROP_PROXY, GETCURRENTCONTEXTINFO, GetCurrentContextInfo, SM_PtrLicenseInteropProxy_PtrType_PtrBool_PtrIntPtr_PtrException_RetVoid) +DEFINE_METHOD(LICENSE_INTEROP_PROXY, SAVEKEYINCURRENTCONTEXT, SaveKeyInCurrentContext, SM_PtrLicenseInteropProxy_IntPtr_PtrException_RetVoid) #endif // FEATURE_COMINTEROP END_ILLINK_FEATURE_SWITCH() @@ -384,9 +384,9 @@ DEFINE_CLASS(GUID, System, Guid) BEGIN_ILLINK_FEATURE_SWITCH(System.Runtime.InteropServices.BuiltInComInterop.IsSupported, true, true) #ifdef FEATURE_COMINTEROP DEFINE_CLASS(VARIANT, System, Variant) -DEFINE_METHOD(VARIANT, CONVERT_OBJECT_TO_VARIANT,MarshalHelperConvertObjectToVariant,SM_Obj_RefComVariant_RetVoid) -DEFINE_METHOD(VARIANT, CAST_VARIANT, MarshalHelperCastVariant, SM_Obj_Int_RefComVariant_RetVoid) -DEFINE_METHOD(VARIANT, CONVERT_VARIANT_TO_OBJECT,MarshalHelperConvertVariantToObject,SM_RefComVariant_RetObject) +DEFINE_METHOD(VARIANT, CONVERT_OBJECT_TO_VARIANT, MarshalHelperConvertObjectToVariant, SM_PtrObj_PtrComVariant_PtrException_RetVoid) +DEFINE_METHOD(VARIANT, CAST_VARIANT, MarshalHelperCastVariant, SM_PtrObj_Int_PtrComVariant_PtrException_RetVoid) +DEFINE_METHOD(VARIANT, CONVERT_VARIANT_TO_OBJECT, MarshalHelperConvertVariantToObject, SM_PtrComVariant_PtrObj_PtrException_RetVoid) #endif // FEATURE_COMINTEROP END_ILLINK_FEATURE_SWITCH() @@ -1045,7 +1045,7 @@ DEFINE_METHOD(STUBHELPERS, GET_HR_EXCEPTION_OBJECT, GetHRExceptionObjec DEFINE_METHOD(STUBHELPERS, GET_PENDING_EXCEPTION_OBJECT, GetPendingExceptionObject, SM_RetException) DEFINE_METHOD(STUBHELPERS, CREATE_CUSTOM_MARSHALER, CreateCustomMarshaler, SM_IntPtr_Int_IntPtr_RetObj) #ifdef FEATURE_COMINTEROP -DEFINE_METHOD(STUBHELPERS, GET_IENUMERATOR_TO_ENUM_VARIANT_MARSHALER, GetIEnumeratorToEnumVariantMarshaler, SM_RetObj) +DEFINE_METHOD(STUBHELPERS, GET_IENUMERATOR_TO_ENUM_VARIANT_MARSHALER, GetIEnumeratorToEnumVariantMarshaler, SM_PtrObj_PtrException_RetVoid) #endif // FEATURE_COMINTEROP DEFINE_METHOD(STUBHELPERS, CHECK_STRING_LENGTH, CheckStringLength, SM_Int_RetVoid) diff --git a/src/coreclr/vm/custommarshalerinfo.cpp b/src/coreclr/vm/custommarshalerinfo.cpp index 2638848324c723..6563aa99a10d43 100644 --- a/src/coreclr/vm/custommarshalerinfo.cpp +++ b/src/coreclr/vm/custommarshalerinfo.cpp @@ -120,8 +120,8 @@ CustomMarshalerInfo* CustomMarshalerInfo::CreateIEnumeratorMarshalerInfo(LoaderH GCX_COOP(); GCPROTECT_BEGIN(IEnumeratorMarshalerObj); - MethodDescCallSite getMarshaler(METHOD__STUBHELPERS__GET_IENUMERATOR_TO_ENUM_VARIANT_MARSHALER); - IEnumeratorMarshalerObj = getMarshaler.Call_RetOBJECTREF(NULL); + UnmanagedCallersOnlyCaller getMarshaler(METHOD__STUBHELPERS__GET_IENUMERATOR_TO_ENUM_VARIANT_MARSHALER); + getMarshaler.InvokeThrowing(&IEnumeratorMarshalerObj); pInfo = new (pHeap) CustomMarshalerInfo(pLoaderAllocator, pLoaderAllocator->AllocateHandle(IEnumeratorMarshalerObj)); diff --git a/src/coreclr/vm/datadescriptor/datadescriptor.inc b/src/coreclr/vm/datadescriptor/datadescriptor.inc index 48a72a4040c40f..b516297965ea79 100644 --- a/src/coreclr/vm/datadescriptor/datadescriptor.inc +++ b/src/coreclr/vm/datadescriptor/datadescriptor.inc @@ -663,14 +663,39 @@ CDAC_TYPE_FIELD(ReadyToRunInfo, /*pointer*/, HotColdMap, cdac_data::DelayLoadMethodCallThunks) CDAC_TYPE_FIELD(ReadyToRunInfo, /*pointer*/, DebugInfoSection, cdac_data::DebugInfoSection) CDAC_TYPE_FIELD(ReadyToRunInfo, /*HashMap*/, EntryPointToMethodDescMap, cdac_data::EntryPointToMethodDescMap) +CDAC_TYPE_FIELD(ReadyToRunInfo, /*pointer*/, LoadedImageBase, cdac_data::LoadedImageBase) +CDAC_TYPE_FIELD(ReadyToRunInfo, /*pointer*/, Composite, cdac_data::Composite) CDAC_TYPE_END(ReadyToRunInfo) CDAC_TYPE_BEGIN(ReadyToRunHeader) -CDAC_TYPE_INDETERMINATE(READYTORUN_HEADER) +CDAC_TYPE_INDETERMINATE(ReadyToRunHeader) CDAC_TYPE_FIELD(ReadyToRunHeader, /*uint16*/, MajorVersion, offsetof(READYTORUN_HEADER, MajorVersion)) CDAC_TYPE_FIELD(ReadyToRunHeader, /*uint16*/, MinorVersion, offsetof(READYTORUN_HEADER, MinorVersion)) CDAC_TYPE_END(ReadyToRunHeader) +CDAC_TYPE_BEGIN(ReadyToRunCoreHeader) +CDAC_TYPE_SIZE(sizeof(READYTORUN_CORE_HEADER)) +CDAC_TYPE_FIELD(ReadyToRunCoreHeader, /*uint32*/, Flags, offsetof(READYTORUN_CORE_HEADER, Flags)) +CDAC_TYPE_FIELD(ReadyToRunCoreHeader, /*uint32*/, NumberOfSections, offsetof(READYTORUN_CORE_HEADER, NumberOfSections)) +CDAC_TYPE_END(ReadyToRunCoreHeader) + +CDAC_TYPE_BEGIN(ReadyToRunCoreInfo) +CDAC_TYPE_INDETERMINATE(ReadyToRunCoreInfo) +CDAC_TYPE_FIELD(ReadyToRunCoreInfo, /*pointer*/, Header, cdac_data::Header) +CDAC_TYPE_END(ReadyToRunCoreInfo) + +CDAC_TYPE_BEGIN(ReadyToRunSection) +CDAC_TYPE_SIZE(sizeof(READYTORUN_SECTION)) +CDAC_TYPE_FIELD(ReadyToRunSection, /*uint32*/, Type, offsetof(READYTORUN_SECTION, Type)) +CDAC_TYPE_FIELD(ReadyToRunSection, /* IMAGE_DATA_DIRECTORY */, Section, offsetof(READYTORUN_SECTION, Section)) +CDAC_TYPE_END(ReadyToRunSection) + +CDAC_TYPE_BEGIN(ExceptionLookupTableEntry) +CDAC_TYPE_SIZE(sizeof(CORCOMPILE_EXCEPTION_LOOKUP_TABLE_ENTRY)) +CDAC_TYPE_FIELD(ExceptionLookupTableEntry, /*uint32*/, MethodStartRVA, offsetof(CORCOMPILE_EXCEPTION_LOOKUP_TABLE_ENTRY, MethodStartRVA)) +CDAC_TYPE_FIELD(ExceptionLookupTableEntry, /*uint32*/, ExceptionInfoRVA, offsetof(CORCOMPILE_EXCEPTION_LOOKUP_TABLE_ENTRY, ExceptionInfoRVA)) +CDAC_TYPE_END(ExceptionLookupTableEntry) + CDAC_TYPE_BEGIN(ImageDataDirectory) CDAC_TYPE_SIZE(sizeof(IMAGE_DATA_DIRECTORY)) CDAC_TYPE_FIELD(ImageDataDirectory, /*uint32*/, VirtualAddress, offsetof(IMAGE_DATA_DIRECTORY, VirtualAddress)) @@ -741,8 +766,34 @@ CDAC_TYPE_FIELD(RealCodeHeader, /*pointer*/, DebugInfo, offsetof(RealCodeHeader, CDAC_TYPE_FIELD(RealCodeHeader, /*pointer*/, GCInfo, offsetof(RealCodeHeader, phdrJitGCInfo)) CDAC_TYPE_FIELD(RealCodeHeader, /*uint32*/, NumUnwindInfos, offsetof(RealCodeHeader, nUnwindInfos)) CDAC_TYPE_FIELD(RealCodeHeader, /* T_RUNTIME_FUNCTION */, UnwindInfos, offsetof(RealCodeHeader, unwindInfos)) +CDAC_TYPE_FIELD(RealCodeHeader, /*pointer*/, JitEHInfo, offsetof(RealCodeHeader, phdrJitEHInfo)) CDAC_TYPE_END(RealCodeHeader) +CDAC_TYPE_BEGIN(EEExceptionClause) +CDAC_TYPE_SIZE(sizeof(EE_ILEXCEPTION_CLAUSE)) +CDAC_TYPE_FIELD(EEExceptionClause, /*uint32*/, Flags, offsetof(EE_ILEXCEPTION_CLAUSE, Flags)) +CDAC_TYPE_FIELD(EEExceptionClause, /*uint32*/, TryStartPC, offsetof(EE_ILEXCEPTION_CLAUSE, TryStartPC)) +CDAC_TYPE_FIELD(EEExceptionClause, /*uint32*/, TryEndPC, offsetof(EE_ILEXCEPTION_CLAUSE, TryEndPC)) +CDAC_TYPE_FIELD(EEExceptionClause, /*uint32*/, HandlerStartPC, offsetof(EE_ILEXCEPTION_CLAUSE, HandlerStartPC)) +CDAC_TYPE_FIELD(EEExceptionClause, /*uint32*/, HandlerEndPC, offsetof(EE_ILEXCEPTION_CLAUSE, HandlerEndPC)) +CDAC_TYPE_FIELD(EEExceptionClause, /*nuint*/, TypeHandle, offsetof(EE_ILEXCEPTION_CLAUSE, TypeHandle)) +CDAC_TYPE_END(EEExceptionClause) + +CDAC_TYPE_BEGIN(R2RExceptionClause) +CDAC_TYPE_SIZE(sizeof(CORCOMPILE_EXCEPTION_CLAUSE)) +CDAC_TYPE_FIELD(R2RExceptionClause, /*uint32*/, Flags, offsetof(CORCOMPILE_EXCEPTION_CLAUSE, Flags)) +CDAC_TYPE_FIELD(R2RExceptionClause, /*uint32*/, TryStartPC, offsetof(CORCOMPILE_EXCEPTION_CLAUSE, TryStartPC)) +CDAC_TYPE_FIELD(R2RExceptionClause, /*uint32*/, TryEndPC, offsetof(CORCOMPILE_EXCEPTION_CLAUSE, TryEndPC)) +CDAC_TYPE_FIELD(R2RExceptionClause, /*uint32*/, HandlerStartPC, offsetof(CORCOMPILE_EXCEPTION_CLAUSE, HandlerStartPC)) +CDAC_TYPE_FIELD(R2RExceptionClause, /*uint32*/, HandlerEndPC, offsetof(CORCOMPILE_EXCEPTION_CLAUSE, HandlerEndPC)) +CDAC_TYPE_FIELD(R2RExceptionClause, /*uint32*/, ClassToken, offsetof(CORCOMPILE_EXCEPTION_CLAUSE, ClassToken)) +CDAC_TYPE_END(R2RExceptionClause) + +CDAC_TYPE_BEGIN(EEILException) +CDAC_TYPE_INDETERMINATE(EEILException) +CDAC_TYPE_FIELD(EEILException, /* EE_ILEXCEPTION_CLAUSE */, Clauses, offsetof(EE_ILEXCEPTION, Clauses)) +CDAC_TYPE_END(EEILException) + CDAC_TYPE_BEGIN(CodeHeapListNode) CDAC_TYPE_FIELD(CodeHeapListNode, /*pointer*/, Next, offsetof(HeapList, hpNext)) CDAC_TYPE_FIELD(CodeHeapListNode, /*pointer*/, StartAddress, offsetof(HeapList, startAddress)) @@ -1115,12 +1166,19 @@ CDAC_TYPE_FIELD(RCW, /*pointer*/, NextRCW, cdac_data::NextRCW) CDAC_TYPE_FIELD(RCW, /*uint32*/, Flags, cdac_data::Flags) CDAC_TYPE_FIELD(RCW, /*pointer*/, CtxCookie, cdac_data::CtxCookie) CDAC_TYPE_FIELD(RCW, /*pointer*/, CtxEntry, cdac_data::CtxEntry) +CDAC_TYPE_FIELD(RCW, /*inline array*/, InterfaceEntries, cdac_data::InterfaceEntries) CDAC_TYPE_END(RCW) CDAC_TYPE_BEGIN(CtxEntry) CDAC_TYPE_INDETERMINATE(CtxEntry) CDAC_TYPE_FIELD(CtxEntry, /*pointer*/, STAThread, cdac_data::STAThread) CDAC_TYPE_END(CtxEntry) + +CDAC_TYPE_BEGIN(InterfaceEntry) +CDAC_TYPE_SIZE(sizeof(InterfaceEntry)) +CDAC_TYPE_FIELD(InterfaceEntry, /*pointer*/, MethodTable, cdac_data::MethodTable) +CDAC_TYPE_FIELD(InterfaceEntry, /*pointer*/, Unknown, cdac_data::Unknown) +CDAC_TYPE_END(InterfaceEntry) #endif // FEATURE_COMINTEROP #ifdef FEATURE_COMWRAPPERS @@ -1306,6 +1364,7 @@ CDAC_GLOBAL_POINTER(TearOffAddRef, &g_cdacTearOffAddRef) CDAC_GLOBAL_POINTER(TearOffAddRefSimple, &g_cdacTearOffAddRefSimple) CDAC_GLOBAL_POINTER(TearOffAddRefSimpleInner, &g_cdacTearOffAddRefSimpleInner) CDAC_GLOBAL_POINTER(RCWCleanupList, &g_pRCWCleanupList) +CDAC_GLOBAL(RCWInterfaceCacheSize, uint32, INTERFACE_ENTRY_CACHE_SIZE) #endif // FEATURE_COMINTEROP // It is important for the subdescriptor pointers to be the last pointers in the global structure. diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index 8be761a468a125..7a5bee2d9cb2af 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -548,12 +548,12 @@ void InvokeCalliStub(CalliStubParam* pParam) pHeader->Invoke(pHeader->Routines, pArgs, pRet, pHeader->TotalStackSize, pContinuationRet); } -void* GetCookieForCalliSig(MetaSig metaSig) +void* GetCookieForCalliSig(MetaSig metaSig, MethodDesc *pContextMD) { STANDARD_VM_CONTRACT; CallStubGenerator callStubGenerator; - return callStubGenerator.GenerateCallStubForSig(metaSig); + return callStubGenerator.GenerateCallStubForSig(metaSig, pContextMD); } // Create call stub for calling interpreted methods from JITted/AOTed code. diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 291ee418eace51..d66dc46f112ce5 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -11419,7 +11419,7 @@ LPVOID CEEInfo::GetCookieForInterpreterCalliSig(CORINFO_SIG_INFO* szMetaSig) #ifdef FEATURE_INTERPRETER // Forward declare the function for mapping MetaSig to a cookie. -void* GetCookieForCalliSig(MetaSig metaSig); +void* GetCookieForCalliSig(MetaSig metaSig, MethodDesc *pContextMD); LPVOID CInterpreterJitInfo::GetCookieForInterpreterCalliSig(CORINFO_SIG_INFO* szMetaSig) { @@ -11438,7 +11438,18 @@ LPVOID CInterpreterJitInfo::GetCookieForInterpreterCalliSig(CORINFO_SIG_INFO* sz _ASSERTE(szMetaSig->isAsyncCall() == sig.IsAsyncCall()); - result = GetCookieForCalliSig(sig); + // When compiling a calli inside an IL stub for a P/Invoke, pass the target + // P/Invoke MethodDesc so ComputeCallStub can detect the Swift calling convention. + MethodDesc* pContextMD = nullptr; + if (m_pMethodBeingCompiled != nullptr && m_pMethodBeingCompiled->IsILStub()) + { + MethodDesc* pTargetMD = m_pMethodBeingCompiled->AsDynamicMethodDesc()->GetILStubResolver()->GetStubTargetMethodDesc(); + if (pTargetMD != nullptr) + { + pContextMD = pTargetMD; + } + } + result = GetCookieForCalliSig(sig, pContextMD); EE_TO_JIT_TRANSITION(); return result; diff --git a/src/coreclr/vm/loongarch64/asmhelpers.S b/src/coreclr/vm/loongarch64/asmhelpers.S index 9f424c39dd30f5..bd6b7beec16007 100644 --- a/src/coreclr/vm/loongarch64/asmhelpers.S +++ b/src/coreclr/vm/loongarch64/asmhelpers.S @@ -637,10 +637,12 @@ NESTED_ENTRY OnHijackTripThread, _TEXT, NoHandler // save any integral return value(s) st.d $a0, $sp, 88 st.d $a1, $sp, 96 + // save async continuation return value + st.d $a2, $sp, 104 // save any FP return value(s) - fst.d $f0, $sp, 104 - fst.d $f1, $sp, 112 + fst.d $f0, $sp, 112 + fst.d $f1, $sp, 120 ori $a0, $sp, 0 bl C_FUNC(OnHijackWorker) @@ -650,10 +652,12 @@ NESTED_ENTRY OnHijackTripThread, _TEXT, NoHandler // restore any integral return value(s) ld.d $a0, $sp, 88 ld.d $a1, $sp, 96 + // restore async continuation return value + ld.d $a2, $sp, 104 // restore any FP return value(s) - fld.d $f0, $sp, 104 - fld.d $f1, $sp, 112 + fld.d $f0, $sp, 112 + fld.d $f1, $sp, 120 EPILOG_RESTORE_REG_PAIR 23, 24, 16 EPILOG_RESTORE_REG_PAIR 25, 26, 32 diff --git a/src/coreclr/vm/loongarch64/cgencpu.h b/src/coreclr/vm/loongarch64/cgencpu.h index eb12a56d8dfd19..039a85d1782c02 100644 --- a/src/coreclr/vm/loongarch64/cgencpu.h +++ b/src/coreclr/vm/loongarch64/cgencpu.h @@ -429,6 +429,11 @@ struct HijackArgs size_t ReturnValue[2]; }; union + { + DWORD64 A2; + size_t AsyncRet; + }; + union { struct { DWORD64 F0; diff --git a/src/coreclr/vm/loongarch64/stubs.cpp b/src/coreclr/vm/loongarch64/stubs.cpp index b974c9511f3a63..8f69312046cb30 100644 --- a/src/coreclr/vm/loongarch64/stubs.cpp +++ b/src/coreclr/vm/loongarch64/stubs.cpp @@ -474,9 +474,11 @@ void HijackFrame::UpdateRegDisplay_Impl(const PREGDISPLAY pRD, bool updateFloats pRD->pCurrentContext->A0 = m_Args->A0; pRD->pCurrentContext->A1 = m_Args->A1; + pRD->pCurrentContext->A2 = m_Args->A2; pRD->volatileCurrContextPointers.A0 = &m_Args->A0; pRD->volatileCurrContextPointers.A1 = &m_Args->A1; + pRD->volatileCurrContextPointers.A2 = &m_Args->A2; pRD->pCurrentContext->S0 = m_Args->S0; pRD->pCurrentContext->S1 = m_Args->S1; diff --git a/src/coreclr/vm/metasig.h b/src/coreclr/vm/metasig.h index 504a481b0b2c92..87a7f81a40c16e 100644 --- a/src/coreclr/vm/metasig.h +++ b/src/coreclr/vm/metasig.h @@ -427,8 +427,22 @@ DEFINE_METASIG_T(SM(PtrResolver_Int_PtrArrByte_PtrVoid_PtrException_RetVoid, P(C DEFINE_METASIG_T(SM(PtrResolver_Int_PtrIntPtr_PtrIntPtr_PtrIntPtr_PtrException_RetVoid, P(C(RESOLVER)) i P(I) P(I) P(I) P(C(EXCEPTION)), v)) DEFINE_METASIG_T(SM(PtrResolver_Int_Int_PtrArrByte_PtrException_RetVoid, P(C(RESOLVER)) i i P(a(b)) P(C(EXCEPTION)), v)) DEFINE_METASIG_T(SM(PtrResolver_Int_PtrStr_PtrException_RetVoid, P(C(RESOLVER)) i P(s) P(C(EXCEPTION)), v)) +#ifdef FEATURE_COMINTEROP +DEFINE_METASIG_T(SM(PtrClass_PtrStr_Int_PtrObj_PtrArrObj_PtrArrBool_PtrArrInt_PtrArrType_PtrType_PtrObj_PtrException_RetVoid, P(C(CLASS)) P(s) i P(j) P(a(j)) P(a(F)) P(a(i)) P(a(C(TYPE))) P(C(TYPE)) P(j) P(C(EXCEPTION)), v)) +DEFINE_METASIG_T(SM(PtrComObject_PtrClass_PtrObj_PtrException_RetVoid, P(C(COM_OBJECT)) P(C(CLASS)) P(j) P(C(EXCEPTION)), v)) +DEFINE_METASIG_T(SM(PtrLicenseInteropProxy_IntPtr_PtrException_RetVoid, P(C(LICENSE_INTEROP_PROXY)) I P(C(EXCEPTION)), v)) +DEFINE_METASIG_T(SM(PtrLicenseInteropProxy_PtrType_PtrBool_PtrIntPtr_PtrException_RetVoid, P(C(LICENSE_INTEROP_PROXY)) P(C(TYPE)) P(F) P(I) P(C(EXCEPTION)), v)) +#endif // FEATURE_COMINTEROP DEFINE_METASIG_T(SM(PtrObj_PtrObj_PtrException_RetVoid, P(j) P(j) P(C(EXCEPTION)), v)) +DEFINE_METASIG_T(SM(PtrObj_PtrObj_PtrObj_PtrException_RetVoid, P(j) P(j) P(j) P(C(EXCEPTION)), v)) DEFINE_METASIG_T(SM(PtrObj_PtrStr_PtrException_RetVoid, P(j) P(s) P(C(EXCEPTION)), v)) +DEFINE_METASIG_T(SM(PtrObj_PtrObj_Int_PtrObj_PtrObj_PtrObj_PtrObj_PtrObj_PtrObj_PtrObj_PtrException_RetVoid, P(j) P(j) i P(j) P(j) P(j) P(j) P(j) P(j) P(j) P(C(EXCEPTION)), v)) +DEFINE_METASIG_T(SM(PtrObj_PtrObj_PtrBool_PtrIntPtr_PtrException_RetVoid, P(j) P(j) P(F) P(I) P(C(EXCEPTION)), v)) +DEFINE_METASIG_T(SM(PtrObj_PtrObj_PtrInt_PtrIntPtr_PtrException_RetVoid, P(j) P(j) P(i) P(I) P(C(EXCEPTION)), v)) +DEFINE_METASIG_T(SM(PtrObj_IntPtr_PtrException_RetVoid, P(j) I P(C(EXCEPTION)), v)) +DEFINE_METASIG_T(SM(PtrObj_PtrComVariant_PtrException_RetVoid, P(j) P(g(COMVARIANT)) P(C(EXCEPTION)), v)) +DEFINE_METASIG_T(SM(PtrObj_Int_PtrComVariant_PtrException_RetVoid, P(j) i P(g(COMVARIANT)) P(C(EXCEPTION)), v)) +DEFINE_METASIG_T(SM(PtrComVariant_PtrObj_PtrException_RetVoid, P(g(COMVARIANT)) P(j) P(C(EXCEPTION)), v)) DEFINE_METASIG_T(SM(PtrException_PtrException_RetVoid, P(C(EXCEPTION)) P(C(EXCEPTION)), v)) DEFINE_METASIG_T(SM(PtrChar_PtrException_PtrObj_PtrException_RetVoid, P(u) P(C(EXCEPTION)) P(j) P(C(EXCEPTION)), v)) DEFINE_METASIG_T(SM(PtrChar_PtrStr_PtrException_RetVoid, P(u) P(s) P(C(EXCEPTION)), v)) diff --git a/src/coreclr/vm/olevariant.cpp b/src/coreclr/vm/olevariant.cpp index 4efc9c79c8bba6..24a00ce910e887 100644 --- a/src/coreclr/vm/olevariant.cpp +++ b/src/coreclr/vm/olevariant.cpp @@ -2158,7 +2158,7 @@ void OleVariant::MarshalOleRefVariantForObject(OBJECTREF *pObj, VARIANT *pOle) } else { - MethodDescCallSite castVariant(METHOD__VARIANT__CAST_VARIANT); + UnmanagedCallersOnlyCaller castVariant(METHOD__VARIANT__CAST_VARIANT); // MarshalOleRefVariantForObjectNoCast has checked that the variant is not an array // so we can use the marshal cast helper to coerce the object to the proper type. @@ -2166,11 +2166,7 @@ void OleVariant::MarshalOleRefVariantForObject(OBJECTREF *pObj, VARIANT *pOle) VariantInit(&vtmp); VARTYPE vt = V_VT(pOle) & ~VT_BYREF; - ARG_SLOT args[3]; - args[0] = ObjToArgSlot(*pObj); - args[1] = (ARG_SLOT)vt; - args[2] = PtrToArgSlot(&vtmp); - castVariant.Call(args); + castVariant.InvokeThrowing(pObj, (INT32)vt, &vtmp); // Managed implementation of CastVariant should either return correct type or throw. _ASSERTE(V_VT(&vtmp) == vt); @@ -2760,10 +2756,8 @@ void OleVariant::MarshalObjectForOleVariantUncommon(const VARIANT *pOle, OBJECTR } else { - MethodDescCallSite convertVariantToObject(METHOD__VARIANT__CONVERT_VARIANT_TO_OBJECT); - ARG_SLOT args[] = { PtrToArgSlot(pOle) }; - SetObjectReference( pObj, - convertVariantToObject.Call_RetOBJECTREF(args) ); + UnmanagedCallersOnlyCaller convertVariantToObject(METHOD__VARIANT__CONVERT_VARIANT_TO_OBJECT); + convertVariantToObject.InvokeThrowing(pOle, pObj); } } @@ -2802,14 +2796,8 @@ void OleVariant::MarshalOleVariantForObjectUncommon(OBJECTREF * const & pObj, VA } else { - MethodDescCallSite convertObjectToVariant(METHOD__VARIANT__CONVERT_OBJECT_TO_VARIANT); - - ARG_SLOT args[] = { - ObjToArgSlot(*pObj), - PtrToArgSlot(pOle), - }; - - convertObjectToVariant.Call(args); + UnmanagedCallersOnlyCaller convertObjectToVariant(METHOD__VARIANT__CONVERT_OBJECT_TO_VARIANT); + convertObjectToVariant.InvokeThrowing(pObj, pOle); } veh.SuppressRelease(); diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index b46b1bfefc7b97..3bc20bab6854d8 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -2307,15 +2307,34 @@ PCODE MethodDesc::DoPrestub(MethodTable *pDispatchingMT, CallerGCMode callerGCMo if (pCode == (PCODE)NULL) { pCode = GetStubForInteropMethod(this); - } +#ifdef FEATURE_INTERPRETER + // Store the IL stub interpreter data on the P/Invoke MethodDesc so the + // interpreter can run the IL stub as a child frame with a single native + // transition. On WASM this is done for all P/Invokes; on ARM64 Apple it + // is needed specifically for Swift to avoid a stale SwiftError (x21). #ifdef FEATURE_PORTABLE_ENTRYPOINTS - // Store the IL Stub interpreter data on the actual - // P/Invoke MethodDesc. - void* ilStubInterpData = PortableEntryPoint::GetInterpreterData(pCode); - _ASSERTE(ilStubInterpData != NULL); - SetInterpreterCode((InterpByteCodeStart*)ilStubInterpData); + void* ilStubInterpData = PortableEntryPoint::GetInterpreterData(pCode); + _ASSERTE(ilStubInterpData != NULL); + SetInterpreterCode((InterpByteCodeStart*)ilStubInterpData); +#else // !FEATURE_PORTABLE_ENTRYPOINTS +#if defined(TARGET_APPLE) && defined(TARGET_ARM64) + { + CorInfoCallConvExtension callConv; + PInvoke::GetCallingConvention_IgnoreErrors(this, &callConv, nullptr); + if (callConv == CorInfoCallConvExtension::Swift) + { + TADDR ilStubInterpCode = GetInterpreterCodeFromInterpreterPrecodeIfPresent(pCode); + if (ilStubInterpCode != (TADDR)pCode) + { + SetInterpreterCode(dac_cast(ilStubInterpCode)); + } + } + } +#endif // TARGET_APPLE && TARGET_ARM64 #endif // FEATURE_PORTABLE_ENTRYPOINTS +#endif // FEATURE_INTERPRETER + } } else if (IsFCall()) { @@ -2405,6 +2424,11 @@ PCODE MethodDesc::DoPrestub(MethodTable *pDispatchingMT, CallerGCMode callerGCMo void* ilStubInterpData = PortableEntryPoint::GetInterpreterData(pCode); _ASSERTE(ilStubInterpData != NULL); SetInterpreterCode((InterpByteCodeStart*)ilStubInterpData); + + // Use this method's own PortableEntryPoint rather than the stub's. + // It is required to maintain 1:1 mapping between MethodDesc and its entrypoint. + pCode = GetPortableEntryPoint(); + PortableEntryPoint::SetInterpreterData(pCode, (PCODE)(TADDR)ilStubInterpData); SetCodeEntryPoint(pCode); #else // !FEATURE_PORTABLE_ENTRYPOINTS if (!GetOrCreatePrecode()->SetTargetInterlocked(pStub->GetEntryPoint())) diff --git a/src/coreclr/vm/readytoruninfo.cpp b/src/coreclr/vm/readytoruninfo.cpp index 3bfb80ca6351fc..02806037960be1 100644 --- a/src/coreclr/vm/readytoruninfo.cpp +++ b/src/coreclr/vm/readytoruninfo.cpp @@ -384,7 +384,7 @@ PTR_MethodDesc ReadyToRunInfo::GetMethodDescForEntryPointInNativeImage(PCODE ent #ifndef DACCESS_COMPILE -void ReadyToRunInfo::SetMethodDescForEntryPointInNativeImage(PCODE entryPoint, MethodDesc *methodDesc) +bool ReadyToRunInfo::SetMethodDescForEntryPointInNativeImage(PCODE entryPoint, MethodDesc *methodDesc) { CONTRACTL { @@ -397,7 +397,9 @@ void ReadyToRunInfo::SetMethodDescForEntryPointInNativeImage(PCODE entryPoint, M if ((TADDR)m_entryPointToMethodDescMap.LookupValue(PCODEToPINSTR(entryPoint), (LPVOID)PCODEToPINSTR(entryPoint)) == (TADDR)INVALIDENTRY) { m_entryPointToMethodDescMap.InsertValue(PCODEToPINSTR(entryPoint), methodDesc); + return true; } + return false; } // A log file to record success/failure of R2R loads. s_r2rLogFile can have the following values: @@ -850,6 +852,7 @@ ReadyToRunInfo::ReadyToRunInfo(Module * pModule, LoaderAllocator* pLoaderAllocat } m_pNativeManifestModule = CreateNativeManifestModule(pLoaderAllocator, pNativeMDImport, pModule, pamTracker); + m_pLoadedImageBase = m_pComposite->GetLayout()->GetBase(); } IMAGE_DATA_DIRECTORY * pRuntimeFunctionsDir = m_pComposite->FindSection(ReadyToRunSectionType::RuntimeFunctions); @@ -1175,13 +1178,10 @@ void ReadyToRunInfo::RegisterResumptionStub(PCODE stubEntryPoint) { STANDARD_VM_CONTRACT; - // Use the entry point hashtable to check if another thread already registered a MethodDesc - if (m_pCompositeInfo->GetMethodDescForEntryPointInNativeImage(stubEntryPoint) != NULL) - return; - AllocMemTracker amTracker; - ILStubCache *pStubCache = m_pModule->GetILStubCache(); + ILStubCache* pStubCache = m_pModule->GetILStubCache(); MethodTable* pStubMT = pStubCache->GetOrCreateStubMethodTable(m_pModule); + LoaderAllocator* pLoaderAllocator = m_pModule->GetLoaderAllocator(); // Resumption stub signature: object(object, ref byte) // This matches BuildResumptionStubSignature in jitinterface.cpp @@ -1194,8 +1194,12 @@ void ReadyToRunInfo::RegisterResumptionStub(PCODE stubEntryPoint) ELEMENT_TYPE_U1 }; + // Use the entry point hashtable to check if another thread already registered a MethodDesc + if (m_pCompositeInfo->GetMethodDescForEntryPointInNativeImage(stubEntryPoint) != NULL) + return; + MethodDesc* pStubMD = pStubCache->CreateR2RBackedILStub( - m_pModule->GetLoaderAllocator(), + pLoaderAllocator, pStubMT, stubEntryPoint, DynamicMethodDesc::StubAsyncResume, @@ -1203,12 +1207,14 @@ void ReadyToRunInfo::RegisterResumptionStub(PCODE stubEntryPoint) sizeof(s_resumptionStubSig), &amTracker); - amTracker.SuppressRelease(); - // Register the stub's entry point so GC can find it during stack walks. // SetMethodDescForEntryPointInNativeImage handles the race - if another thread - // already registered a MethodDesc for this entry point, ours is simply discarded. - m_pCompositeInfo->SetMethodDescForEntryPointInNativeImage(stubEntryPoint, pStubMD); + // already registered a MethodDesc for this entry point, ours is simply discarded + // and the AllocMemTracker will back out the allocation on destruction. + if (m_pCompositeInfo->SetMethodDescForEntryPointInNativeImage(stubEntryPoint, pStubMD)) + { + amTracker.SuppressRelease(); + } } PCODE ReadyToRunInfo::GetEntryPoint(MethodDesc * pMD, PrepareCodeConfig* pConfig, BOOL fFixups) diff --git a/src/coreclr/vm/readytoruninfo.h b/src/coreclr/vm/readytoruninfo.h index 989c5817f3419a..ecc4198f556060 100644 --- a/src/coreclr/vm/readytoruninfo.h +++ b/src/coreclr/vm/readytoruninfo.h @@ -30,6 +30,7 @@ class ReadyToRunCoreInfo PTR_ReadyToRunLoadedImage m_pLayout; PTR_READYTORUN_CORE_HEADER m_pCoreHeader; Volatile m_fForbidLoadILBodyFixups; + friend struct ::cdac_data; public: ReadyToRunCoreInfo(); @@ -47,6 +48,12 @@ class ReadyToRunCoreInfo } }; +template<> +struct cdac_data +{ + static constexpr size_t Header = offsetof(ReadyToRunCoreInfo, m_pCoreHeader); +}; + typedef DPTR(class ReadyToRunInfo) PTR_ReadyToRunInfo; typedef DPTR(class ReadyToRunCoreInfo) PTR_ReadyToRunCoreInfo; @@ -144,6 +151,7 @@ class ReadyToRunInfo PTR_PersistentInlineTrackingMapR2R m_pCrossModulePersistentInlineTrackingMap; PTR_ReadyToRunInfo m_pNextR2RForUnrelatedCode; + TADDR m_pLoadedImageBase; public: ReadyToRunInfo(Module * pModule, LoaderAllocator* pLoaderAllocator, READYTORUN_HEADER * pHeader, NativeImage * pNativeImage, ReadyToRunLoadedImage * pLayout, AllocMemTracker *pamTracker); @@ -339,7 +347,7 @@ class ReadyToRunInfo BOOL CompareTypeNameOfTokens(mdToken mdToken1, IMDInternalImport * pImport1, ModuleBase *pModule1, mdToken mdToken2, IMDInternalImport * pImport2, ModuleBase *pModule2); PTR_MethodDesc GetMethodDescForEntryPointInNativeImage(PCODE entryPoint); - void SetMethodDescForEntryPointInNativeImage(PCODE entryPoint, PTR_MethodDesc methodDesc); + bool SetMethodDescForEntryPointInNativeImage(PCODE entryPoint, PTR_MethodDesc methodDesc); PTR_ReadyToRunCoreInfo GetComponentInfo() { return dac_cast(&m_component); } @@ -358,6 +366,8 @@ struct cdac_data static constexpr size_t DelayLoadMethodCallThunks = offsetof(ReadyToRunInfo, m_pSectionDelayLoadMethodCallThunks); static constexpr size_t DebugInfoSection = offsetof(ReadyToRunInfo, m_pSectionDebugInfo); static constexpr size_t EntryPointToMethodDescMap = offsetof(ReadyToRunInfo, m_entryPointToMethodDescMap); + static constexpr size_t LoadedImageBase = offsetof(ReadyToRunInfo, m_pLoadedImageBase); + static constexpr size_t Composite = offsetof(ReadyToRunInfo, m_pComposite); }; class DynamicHelpers diff --git a/src/coreclr/vm/runtimecallablewrapper.cpp b/src/coreclr/vm/runtimecallablewrapper.cpp index db58879480b80a..076a4a043fa7af 100644 --- a/src/coreclr/vm/runtimecallablewrapper.cpp +++ b/src/coreclr/vm/runtimecallablewrapper.cpp @@ -137,20 +137,16 @@ IUnknown *ComClassFactory::CreateInstanceFromClassFactory(IClassFactory *pClassF GCPROTECT_BEGIN(gc); // Create an instance of the object - MethodDescCallSite createObj(METHOD__LICENSE_INTEROP_PROXY__CREATE); - gc.pProxy = createObj.Call_RetOBJECTREF(NULL); + UnmanagedCallersOnlyCaller createObj(METHOD__LICENSE_INTEROP_PROXY__CREATE); + createObj.InvokeThrowing(&gc.pProxy); gc.pType = rth.GetManagedClassObject(); // Query the current licensing context - MethodDescCallSite getCurrentContextInfo(METHOD__LICENSE_INTEROP_PROXY__GETCURRENTCONTEXTINFO, &gc.pProxy); + UnmanagedCallersOnlyCaller getCurrentContextInfo(METHOD__LICENSE_INTEROP_PROXY__GETCURRENTCONTEXTINFO); CLR_BOOL fDesignTime = FALSE; - ARG_SLOT args[4]; - args[0] = ObjToArgSlot(gc.pProxy); - args[1] = ObjToArgSlot(gc.pType); - args[2] = (ARG_SLOT)&fDesignTime; - args[3] = (ARG_SLOT)(BSTR*)&bstrKey; - - getCurrentContextInfo.Call(args); + INT_PTR bstrKeyRaw = NULL; + getCurrentContextInfo.InvokeThrowing(&gc.pProxy, &gc.pType, &fDesignTime, &bstrKeyRaw); + bstrKey = (BSTR)bstrKeyRaw; if (fDesignTime) { @@ -181,11 +177,9 @@ IUnknown *ComClassFactory::CreateInstanceFromClassFactory(IClassFactory *pClassF // Store the requested license key if (SUCCEEDED(hr)) { - MethodDescCallSite saveKeyInCurrentContext(METHOD__LICENSE_INTEROP_PROXY__SAVEKEYINCURRENTCONTEXT, &gc.pProxy); - - args[0] = ObjToArgSlot(gc.pProxy); - args[1] = (ARG_SLOT)(BSTR)bstrKey; - saveKeyInCurrentContext.Call(args); + UnmanagedCallersOnlyCaller saveKeyInCurrentContext(METHOD__LICENSE_INTEROP_PROXY__SAVEKEYINCURRENTCONTEXT); + BSTR bstrKeyValue = (BSTR)bstrKey; + saveKeyInCurrentContext.InvokeThrowing(&gc.pProxy, reinterpret_cast(bstrKeyValue)); } } diff --git a/src/coreclr/vm/runtimecallablewrapper.h b/src/coreclr/vm/runtimecallablewrapper.h index 54ad924248ffe8..fee5c74177ee78 100644 --- a/src/coreclr/vm/runtimecallablewrapper.h +++ b/src/coreclr/vm/runtimecallablewrapper.h @@ -542,7 +542,6 @@ private : // IUnkEntry needs to access m_UnkEntry field friend IUnkEntry; - // cdac_data needs access to m_UnkEntry friend struct ::cdac_data; private : @@ -591,6 +590,7 @@ struct cdac_data static constexpr size_t Flags = offsetof(RCW, m_Flags); static constexpr size_t CtxCookie = offsetof(RCW, m_UnkEntry) + offsetof(IUnkEntry, m_pCtxCookie); static constexpr size_t CtxEntry = offsetof(RCW, m_UnkEntry) + offsetof(IUnkEntry, m_pCtxEntry); + static constexpr size_t InterfaceEntries = offsetof(RCW, m_aInterfaceEntries); }; inline RCW::CreationFlags operator|(RCW::CreationFlags lhs, RCW::CreationFlags rhs) diff --git a/src/coreclr/vm/wasm/helpers.cpp b/src/coreclr/vm/wasm/helpers.cpp index 614743ca348830..afb6d2f739a8b8 100644 --- a/src/coreclr/vm/wasm/helpers.cpp +++ b/src/coreclr/vm/wasm/helpers.cpp @@ -727,7 +727,7 @@ namespace } } -void* GetCookieForCalliSig(MetaSig metaSig) +void* GetCookieForCalliSig(MetaSig metaSig, MethodDesc *pContextMD) { STANDARD_VM_CONTRACT; @@ -766,7 +766,7 @@ void* GetUnmanagedCallersOnlyThunk(MethodDesc* pMD) void InvokeManagedMethod(ManagedMethodParam *pParam) { MetaSig sig(pParam->pMD); - void* cookie = GetCookieForCalliSig(sig); + void* cookie = GetCookieForCalliSig(sig, pParam->pMD); _ASSERTE(cookie != NULL); diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Pipe.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Pipe.cs index 3574339966880d..f6fb59906e32c2 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Pipe.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Pipe.cs @@ -12,6 +12,8 @@ internal static partial class Sys internal enum PipeFlags { O_CLOEXEC = 0x0010, + O_NONBLOCK_READ = 0x0400, + O_NONBLOCK_WRITE = 0x0800, } /// diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Read.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Read.cs index 76f21fc80496e0..66d3913cdc7ae9 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Read.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Read.cs @@ -19,5 +19,8 @@ internal static partial class Sys /// [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_Read", SetLastError = true)] internal static unsafe partial int Read(SafeHandle fd, byte* buffer, int count); + + [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_ReadFromNonblocking", SetLastError = true)] + internal static unsafe partial int ReadFromNonblocking(SafeHandle fd, byte* buffer, int count); } } diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Write.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Write.cs index 749b34b2e0ca72..059debf52c7684 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Write.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Write.cs @@ -22,5 +22,8 @@ internal static partial class Sys [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_Write", SetLastError = true)] internal static unsafe partial int Write(IntPtr fd, byte* buffer, int bufferSize); + + [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_WriteToNonblocking", SetLastError = true)] + internal static unsafe partial int WriteToNonblocking(SafeHandle fd, byte* buffer, int bufferSize); } } diff --git a/src/libraries/Common/src/Interop/Windows/IpHlpApi/Interop.NetworkInformation.cs b/src/libraries/Common/src/Interop/Windows/IpHlpApi/Interop.NetworkInformation.cs index b3e318108b377f..983e3d80435967 100644 --- a/src/libraries/Common/src/Interop/Windows/IpHlpApi/Interop.NetworkInformation.cs +++ b/src/libraries/Common/src/Interop/Windows/IpHlpApi/Interop.NetworkInformation.cs @@ -2,11 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Buffers.Binary; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using Microsoft.Win32.SafeHandles; internal static partial class Interop { @@ -379,59 +380,48 @@ internal unsafe struct MibIcmpStatsEx [StructLayout(LayoutKind.Sequential)] internal struct MibTcpTable { - internal uint numberOfEntries; + internal uint NumEntries; + internal MibTcpRow FirstEntry; } [StructLayout(LayoutKind.Sequential)] internal struct MibTcpRow { - internal TcpState state; - internal uint localAddr; - internal byte localPort1; - internal byte localPort2; - // Ports are only 16 bit values (in network WORD order, 3,4,1,2). - // There are reports where the high order bytes have garbage in them. - internal byte ignoreLocalPort3; - internal byte ignoreLocalPort4; - internal uint remoteAddr; - internal byte remotePort1; - internal byte remotePort2; + internal TcpState State; + internal uint LocalAddr; + internal uint LocalPort; + internal uint RemoteAddr; + internal uint RemotePort; + // Ports are only 16 bit values (in network WORD order, 3,4,1,2). // There are reports where the high order bytes have garbage in them. - internal byte ignoreRemotePort3; - internal byte ignoreRemotePort4; + internal readonly IPEndPoint LocalEndPoint => new(LocalAddr, BinaryPrimitives.ReverseEndianness((ushort)LocalPort)); + internal readonly IPEndPoint RemoteEndPoint => new(RemoteAddr, BinaryPrimitives.ReverseEndianness((ushort)RemotePort)); } [StructLayout(LayoutKind.Sequential)] internal struct MibTcp6TableOwnerPid { - internal uint numberOfEntries; + internal uint NumEntries; + internal MibTcp6RowOwnerPid FirstEntry; } [StructLayout(LayoutKind.Sequential)] - internal unsafe struct MibTcp6RowOwnerPid + internal struct MibTcp6RowOwnerPid { - internal fixed byte localAddr[16]; - internal uint localScopeId; - internal byte localPort1; - internal byte localPort2; - // Ports are only 16 bit values (in network WORD order, 3,4,1,2). - // There are reports where the high order bytes have garbage in them. - internal byte ignoreLocalPort3; - internal byte ignoreLocalPort4; - internal fixed byte remoteAddr[16]; - internal uint remoteScopeId; - internal byte remotePort1; - internal byte remotePort2; + internal InlineArray16 LocalAddr; + internal uint LocalScopeId; + internal uint LocalPort; + internal InlineArray16 RemoteAddr; + internal uint RemoteScopeId; + internal uint RemotePort; + internal TcpState State; + internal uint OwningPid; + // Ports are only 16 bit values (in network WORD order, 3,4,1,2). // There are reports where the high order bytes have garbage in them. - internal byte ignoreRemotePort3; - internal byte ignoreRemotePort4; - internal TcpState state; - internal uint owningPid; - - internal ReadOnlySpan localAddrAsSpan => MemoryMarshal.CreateSpan(ref localAddr[0], 16); - internal ReadOnlySpan remoteAddrAsSpan => MemoryMarshal.CreateSpan(ref remoteAddr[0], 16); + internal readonly IPEndPoint LocalEndPoint => new(new IPAddress(LocalAddr, LocalScopeId), BinaryPrimitives.ReverseEndianness((ushort)LocalPort)); + internal readonly IPEndPoint RemoteEndPoint => new(new IPAddress(RemoteAddr, RemoteScopeId), BinaryPrimitives.ReverseEndianness((ushort)RemotePort)); } internal enum TcpTableClass @@ -450,19 +440,19 @@ internal enum TcpTableClass [StructLayout(LayoutKind.Sequential)] internal struct MibUdpTable { - internal uint numberOfEntries; + internal uint NumEntries; + internal MibUdpRow FirstEntry; } [StructLayout(LayoutKind.Sequential)] internal struct MibUdpRow { - internal uint localAddr; - internal byte localPort1; - internal byte localPort2; + internal uint LocalAddr; + internal uint LocalPort; + // Ports are only 16 bit values (in network WORD order, 3,4,1,2). // There are reports where the high order bytes have garbage in them. - internal byte ignoreLocalPort3; - internal byte ignoreLocalPort4; + internal readonly IPEndPoint LocalEndPoint => new(LocalAddr, BinaryPrimitives.ReverseEndianness((ushort)LocalPort)); } internal enum UdpTableClass @@ -475,23 +465,21 @@ internal enum UdpTableClass [StructLayout(LayoutKind.Sequential)] internal struct MibUdp6TableOwnerPid { - internal uint numberOfEntries; + internal uint NumEntries; + internal MibUdp6RowOwnerPid FirstEntry; } [StructLayout(LayoutKind.Sequential)] - internal unsafe struct MibUdp6RowOwnerPid + internal struct MibUdp6RowOwnerPid { - internal fixed byte localAddr[16]; - internal uint localScopeId; - internal byte localPort1; - internal byte localPort2; + internal InlineArray16 LocalAddr; + internal uint LocalScopeId; + internal uint LocalPort; + internal uint OwningPid; + // Ports are only 16 bit values (in network WORD order, 3,4,1,2). // There are reports where the high order bytes have garbage in them. - internal byte ignoreLocalPort3; - internal byte ignoreLocalPort4; - internal uint owningPid; - - internal ReadOnlySpan localAddrAsSpan => MemoryMarshal.CreateSpan(ref localAddr[0], 16); + internal readonly IPEndPoint LocalEndPoint => new(new IPAddress(LocalAddr, LocalScopeId), BinaryPrimitives.ReverseEndianness((ushort)LocalPort)); } [LibraryImport(Interop.Libraries.IpHlpApi)] @@ -523,16 +511,10 @@ internal static unsafe partial uint GetAdaptersAddresses( [LibraryImport(Interop.Libraries.IpHlpApi)] internal static partial uint GetIcmpStatisticsEx(out MibIcmpInfoEx statistics, AddressFamily family); - [LibraryImport(Interop.Libraries.IpHlpApi)] - internal static unsafe partial uint GetTcpTable(IntPtr pTcpTable, uint* dwOutBufLen, [MarshalAs(UnmanagedType.Bool)] bool order); - [LibraryImport(Interop.Libraries.IpHlpApi)] internal static unsafe partial uint GetExtendedTcpTable(IntPtr pTcpTable, uint* dwOutBufLen, [MarshalAs(UnmanagedType.Bool)] bool order, uint IPVersion, TcpTableClass tableClass, uint reserved); - [LibraryImport(Interop.Libraries.IpHlpApi)] - internal static unsafe partial uint GetUdpTable(IntPtr pUdpTable, uint* dwOutBufLen, [MarshalAs(UnmanagedType.Bool)] bool order); - [LibraryImport(Interop.Libraries.IpHlpApi)] internal static unsafe partial uint GetExtendedUdpTable(IntPtr pUdpTable, uint* dwOutBufLen, [MarshalAs(UnmanagedType.Bool)] bool order, uint IPVersion, UdpTableClass tableClass, uint reserved); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreateNamedPipe_SafeFileHandle.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreateNamedPipe_SafeFileHandle.cs new file mode 100644 index 00000000000000..91af8b6f52da39 --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreateNamedPipe_SafeFileHandle.cs @@ -0,0 +1,22 @@ +// 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.InteropServices; +using Microsoft.Win32.SafeHandles; + +internal static partial class Interop +{ + internal static partial class Kernel32 + { + [LibraryImport(Libraries.Kernel32, EntryPoint = "CreateNamedPipeW", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] + internal static partial SafeFileHandle CreateNamedPipeFileHandle( + string pipeName, + int openMode, + int pipeMode, + int maxInstances, + int outBufferSize, + int inBufferSize, + int defaultTimeout, + ref SECURITY_ATTRIBUTES securityAttributes); + } +} diff --git a/src/libraries/Common/src/SourceGenerators/DiagnosticInfo.cs b/src/libraries/Common/src/SourceGenerators/DiagnosticInfo.cs deleted file mode 100644 index 74f44f99c62baa..00000000000000 --- a/src/libraries/Common/src/SourceGenerators/DiagnosticInfo.cs +++ /dev/null @@ -1,60 +0,0 @@ -// 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.Linq; -using System.Numerics.Hashing; -using Microsoft.CodeAnalysis; - -namespace SourceGenerators; - -/// -/// Descriptor for diagnostic instances using structural equality comparison. -/// Provides a work-around for https://github.com/dotnet/roslyn/issues/68291. -/// -internal readonly struct DiagnosticInfo : IEquatable -{ - public DiagnosticDescriptor Descriptor { get; private init; } - public object?[] MessageArgs { get; private init; } - public Location? Location { get; private init; } - - public static DiagnosticInfo Create(DiagnosticDescriptor descriptor, Location? location, object?[]? messageArgs) - { - Location? trimmedLocation = location is null ? null : GetTrimmedLocation(location); - - return new DiagnosticInfo - { - Descriptor = descriptor, - Location = trimmedLocation, - MessageArgs = messageArgs ?? Array.Empty() - }; - - // Creates a copy of the Location instance that does not capture a reference to Compilation. - static Location GetTrimmedLocation(Location location) - => Location.Create(location.SourceTree?.FilePath ?? "", location.SourceSpan, location.GetLineSpan().Span); - } - - public Diagnostic CreateDiagnostic() - => Diagnostic.Create(Descriptor, Location, MessageArgs); - - public override readonly bool Equals(object? obj) => obj is DiagnosticInfo info && Equals(info); - - public readonly bool Equals(DiagnosticInfo other) - { - return Descriptor.Equals(other.Descriptor) && - MessageArgs.SequenceEqual(other.MessageArgs) && - Location == other.Location; - } - - public override readonly int GetHashCode() - { - int hashCode = Descriptor.GetHashCode(); - foreach (object? messageArg in MessageArgs) - { - hashCode = HashHelpers.Combine(hashCode, messageArg?.GetHashCode() ?? 0); - } - - hashCode = HashHelpers.Combine(hashCode, Location?.GetHashCode() ?? 0); - return hashCode; - } -} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/AlgorithmIdentifierAsn.manual.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/AlgorithmIdentifierAsn.manual.cs index 058093035af1ec..b8a5da88b67b47 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/AlgorithmIdentifierAsn.manual.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/AlgorithmIdentifierAsn.manual.cs @@ -59,4 +59,59 @@ internal static bool RepresentsNull(ReadOnlyMemory? parameters) return span[1] == 0; } } + + internal ref partial struct ValueAlgorithmIdentifierAsn + { + internal static ReadOnlySpan ExplicitDerNull => [0x05, 0x00]; + + internal bool Equals(ref readonly ValueAlgorithmIdentifierAsn other) + { + if (Algorithm != other.Algorithm) + { + return false; + } + + bool isNull = RepresentsNull(ref this); + bool isOtherNull = RepresentsNull(in other); + + if (isNull != isOtherNull) + { + return false; + } + + if (isNull) + { + return true; + } + + return Parameters.SequenceEqual(other.Parameters); + } + + internal readonly bool HasNullEquivalentParameters() + { + return RepresentsNull(in this); + } + + internal static bool RepresentsNull(ref readonly ValueAlgorithmIdentifierAsn algorithm) + { + if (!algorithm.HasParameters) + { + return true; + } + + ReadOnlySpan span = algorithm.Parameters; + + if (span.Length != 2) + { + return false; + } + + if (span[0] != 0x05) + { + return false; + } + + return span[1] == 0; + } + } } diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/AlgorithmIdentifierAsn.xml b/src/libraries/Common/src/System/Security/Cryptography/Asn1/AlgorithmIdentifierAsn.xml index 980d0f2d6fb0e2..29cfb3d2d04563 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/AlgorithmIdentifierAsn.xml +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/AlgorithmIdentifierAsn.xml @@ -2,7 +2,8 @@ + namespace="System.Security.Cryptography.Asn1" + emitType="both"> - + \ No newline at end of file diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/AttributeAsn.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/AttributeAsn.xml.cs index ad217282851c8c..a25b39a22327c6 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/AttributeAsn.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/AttributeAsn.xml.cs @@ -119,4 +119,116 @@ private static void DecodeCore(ref ValueAsnReader reader, Asn1Tag expectedTag, R sequenceReader.ThrowIfNotEmpty(); } } + + [StructLayout(LayoutKind.Sequential)] + internal ref partial struct ValueAttributeAsn + { + internal string AttrType; + internal ReadOnlySpan AttrValues; + + internal static void Decode(ReadOnlySpan encoded, AsnEncodingRules ruleSet, out ValueAttributeAsn decoded) + { + Decode(Asn1Tag.Sequence, encoded, ruleSet, out decoded); + } + + internal static void Decode(Asn1Tag expectedTag, ReadOnlySpan encoded, AsnEncodingRules ruleSet, out ValueAttributeAsn decoded) + { + try + { + ValueAsnReader reader = new ValueAsnReader(encoded, ruleSet); + + DecodeCore(ref reader, expectedTag, out decoded); + reader.ThrowIfNotEmpty(); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + internal static void Decode(scoped ref ValueAsnReader reader, out ValueAttributeAsn decoded) + { + Decode(ref reader, Asn1Tag.Sequence, out decoded); + } + + internal static void Decode(scoped ref ValueAsnReader reader, Asn1Tag expectedTag, out ValueAttributeAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(scoped ref ValueAsnReader reader, Asn1Tag expectedTag, out ValueAttributeAsn decoded) + { + decoded = default; + ValueAsnReader sequenceReader = reader.ReadSequence(expectedTag); + + decoded.AttrType = sequenceReader.ReadObjectIdentifier(); + + if (!sequenceReader.PeekTag().HasSameClassAndValue(Asn1Tag.SetOf)) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + decoded.AttrValues = sequenceReader.ReadEncodedValue(); + + sequenceReader.ThrowIfNotEmpty(); + } + + + internal AttrValuesEnumerable GetAttrValues(AsnEncodingRules ruleSet) + { + return new AttrValuesEnumerable(AttrValues, ruleSet); + } + + internal readonly ref struct AttrValuesEnumerable + { + private readonly ReadOnlySpan _encoded; + private readonly AsnEncodingRules _ruleSet; + + internal AttrValuesEnumerable(ReadOnlySpan encoded, AsnEncodingRules ruleSet) + { + _encoded = encoded; + _ruleSet = ruleSet; + } + + public Enumerator GetEnumerator() => new Enumerator(_encoded, _ruleSet); + + internal ref struct Enumerator + { + private ValueAsnReader _reader; + private ReadOnlySpan _current; + + internal Enumerator(ReadOnlySpan encoded, AsnEncodingRules ruleSet) + { + if (!encoded.IsEmpty) + { + ValueAsnReader outerReader = new ValueAsnReader(encoded, ruleSet); + _reader = outerReader.ReadSetOf(); + outerReader.ThrowIfNotEmpty(); + } + + _current = default; + } + + public ReadOnlySpan Current => _current; + + public bool MoveNext() + { + if (!_reader.HasData) + { + return false; + } + + _current = _reader.ReadEncodedValue(); + return true; + } + } + } + } } diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/GeneralSubtreeAsn.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/GeneralSubtreeAsn.xml.cs index 66366c549c4323..c8f2f2a4036c9c 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/GeneralSubtreeAsn.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/GeneralSubtreeAsn.xml.cs @@ -8,22 +8,17 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 { - [StructLayout(LayoutKind.Sequential)] - internal partial struct GeneralSubtreeAsn + file static class SharedGeneralSubtreeAsn { - private static ReadOnlySpan DefaultMinimum => [0x02, 0x01, 0x00]; - - internal System.Security.Cryptography.Asn1.GeneralNameAsn Base; - internal int Minimum; - internal int? Maximum; + internal static ReadOnlySpan DefaultMinimum => [0x02, 0x01, 0x00]; #if DEBUG - static GeneralSubtreeAsn() + static SharedGeneralSubtreeAsn() { GeneralSubtreeAsn decoded = default; ValueAsnReader reader; - reader = new ValueAsnReader(DefaultMinimum, AsnEncodingRules.DER); + reader = new ValueAsnReader(SharedGeneralSubtreeAsn.DefaultMinimum, AsnEncodingRules.DER); if (!reader.TryReadInt32(out decoded.Minimum)) { @@ -33,6 +28,14 @@ static GeneralSubtreeAsn() reader.ThrowIfNotEmpty(); } #endif + } + + [StructLayout(LayoutKind.Sequential)] + internal partial struct GeneralSubtreeAsn + { + internal System.Security.Cryptography.Asn1.GeneralNameAsn Base; + internal int Minimum; + internal int? Maximum; internal readonly void Encode(AsnWriter writer) { @@ -51,7 +54,7 @@ internal readonly void Encode(AsnWriter writer, Asn1Tag tag) AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER, initialCapacity: AsnManagedIntegerDerMaxEncodeSize); tmp.WriteInteger(Minimum); - if (!tmp.EncodedValueEquals(DefaultMinimum)) + if (!tmp.EncodedValueEquals(SharedGeneralSubtreeAsn.DefaultMinimum)) { writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); tmp.CopyTo(writer); @@ -130,7 +133,7 @@ private static void DecodeCore(ref ValueAsnReader reader, Asn1Tag expectedTag, R } else { - defaultReader = new ValueAsnReader(DefaultMinimum, AsnEncodingRules.DER); + defaultReader = new ValueAsnReader(SharedGeneralSubtreeAsn.DefaultMinimum, AsnEncodingRules.DER); if (!defaultReader.TryReadInt32(out decoded.Minimum)) { diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/OaepParamsAsn.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/OaepParamsAsn.xml.cs index e2b2098b80911e..1bfeabc1e0347a 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/OaepParamsAsn.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/OaepParamsAsn.xml.cs @@ -8,39 +8,42 @@ namespace System.Security.Cryptography.Asn1 { - [StructLayout(LayoutKind.Sequential)] - internal partial struct OaepParamsAsn + file static class SharedOaepParamsAsn { - private static ReadOnlySpan DefaultHashFunc => [0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00]; + internal static ReadOnlySpan DefaultHashFunc => [0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00]; - private static ReadOnlySpan DefaultMaskGenFunc => [0x30, 0x16, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x08, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00]; + internal static ReadOnlySpan DefaultMaskGenFunc => [0x30, 0x16, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x08, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00]; - private static ReadOnlySpan DefaultPSourceFunc => [0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x09, 0x04, 0x00]; - - internal System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn HashFunc; - internal System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn MaskGenFunc; - internal System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn PSourceFunc; + internal static ReadOnlySpan DefaultPSourceFunc => [0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x09, 0x04, 0x00]; #if DEBUG - static OaepParamsAsn() + static SharedOaepParamsAsn() { OaepParamsAsn decoded = default; ReadOnlyMemory rebind = default; ValueAsnReader reader; - reader = new ValueAsnReader(DefaultHashFunc, AsnEncodingRules.DER); + reader = new ValueAsnReader(SharedOaepParamsAsn.DefaultHashFunc, AsnEncodingRules.DER); System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn.Decode(ref reader, rebind, out decoded.HashFunc); reader.ThrowIfNotEmpty(); - reader = new ValueAsnReader(DefaultMaskGenFunc, AsnEncodingRules.DER); + reader = new ValueAsnReader(SharedOaepParamsAsn.DefaultMaskGenFunc, AsnEncodingRules.DER); System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn.Decode(ref reader, rebind, out decoded.MaskGenFunc); reader.ThrowIfNotEmpty(); - reader = new ValueAsnReader(DefaultPSourceFunc, AsnEncodingRules.DER); + reader = new ValueAsnReader(SharedOaepParamsAsn.DefaultPSourceFunc, AsnEncodingRules.DER); System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn.Decode(ref reader, rebind, out decoded.PSourceFunc); reader.ThrowIfNotEmpty(); } #endif + } + + [StructLayout(LayoutKind.Sequential)] + internal partial struct OaepParamsAsn + { + internal System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn HashFunc; + internal System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn MaskGenFunc; + internal System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn PSourceFunc; internal readonly void Encode(AsnWriter writer) { @@ -57,7 +60,7 @@ internal readonly void Encode(AsnWriter writer, Asn1Tag tag) AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER); HashFunc.Encode(tmp); - if (!tmp.EncodedValueEquals(DefaultHashFunc)) + if (!tmp.EncodedValueEquals(SharedOaepParamsAsn.DefaultHashFunc)) { writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); tmp.CopyTo(writer); @@ -71,7 +74,7 @@ internal readonly void Encode(AsnWriter writer, Asn1Tag tag) AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER); MaskGenFunc.Encode(tmp); - if (!tmp.EncodedValueEquals(DefaultMaskGenFunc)) + if (!tmp.EncodedValueEquals(SharedOaepParamsAsn.DefaultMaskGenFunc)) { writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); tmp.CopyTo(writer); @@ -85,7 +88,7 @@ internal readonly void Encode(AsnWriter writer, Asn1Tag tag) AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER); PSourceFunc.Encode(tmp); - if (!tmp.EncodedValueEquals(DefaultPSourceFunc)) + if (!tmp.EncodedValueEquals(SharedOaepParamsAsn.DefaultPSourceFunc)) { writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); tmp.CopyTo(writer); @@ -150,7 +153,7 @@ private static void DecodeCore(ref ValueAsnReader reader, Asn1Tag expectedTag, R } else { - defaultReader = new ValueAsnReader(DefaultHashFunc, AsnEncodingRules.DER); + defaultReader = new ValueAsnReader(SharedOaepParamsAsn.DefaultHashFunc, AsnEncodingRules.DER); System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn.Decode(ref defaultReader, rebind, out decoded.HashFunc); } @@ -163,7 +166,7 @@ private static void DecodeCore(ref ValueAsnReader reader, Asn1Tag expectedTag, R } else { - defaultReader = new ValueAsnReader(DefaultMaskGenFunc, AsnEncodingRules.DER); + defaultReader = new ValueAsnReader(SharedOaepParamsAsn.DefaultMaskGenFunc, AsnEncodingRules.DER); System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn.Decode(ref defaultReader, rebind, out decoded.MaskGenFunc); } @@ -176,7 +179,7 @@ private static void DecodeCore(ref ValueAsnReader reader, Asn1Tag expectedTag, R } else { - defaultReader = new ValueAsnReader(DefaultPSourceFunc, AsnEncodingRules.DER); + defaultReader = new ValueAsnReader(SharedOaepParamsAsn.DefaultPSourceFunc, AsnEncodingRules.DER); System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn.Decode(ref defaultReader, rebind, out decoded.PSourceFunc); } diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pbkdf2Params.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pbkdf2Params.xml.cs index 77496a88650038..0012541c13dbf3 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pbkdf2Params.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pbkdf2Params.xml.cs @@ -8,28 +8,31 @@ namespace System.Security.Cryptography.Asn1 { - [StructLayout(LayoutKind.Sequential)] - internal partial struct Pbkdf2Params + file static class SharedPbkdf2Params { - private static ReadOnlySpan DefaultPrf => [0x30, 0x0C, 0x06, 0x08, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x07, 0x05, 0x00]; - - internal System.Security.Cryptography.Asn1.Pbkdf2SaltChoice Salt; - internal int IterationCount; - internal int? KeyLength; - internal System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn Prf; + internal static ReadOnlySpan DefaultPrf => [0x30, 0x0C, 0x06, 0x08, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x07, 0x05, 0x00]; #if DEBUG - static Pbkdf2Params() + static SharedPbkdf2Params() { Pbkdf2Params decoded = default; ReadOnlyMemory rebind = default; ValueAsnReader reader; - reader = new ValueAsnReader(DefaultPrf, AsnEncodingRules.DER); + reader = new ValueAsnReader(SharedPbkdf2Params.DefaultPrf, AsnEncodingRules.DER); System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn.Decode(ref reader, rebind, out decoded.Prf); reader.ThrowIfNotEmpty(); } #endif + } + + [StructLayout(LayoutKind.Sequential)] + internal partial struct Pbkdf2Params + { + internal System.Security.Cryptography.Asn1.Pbkdf2SaltChoice Salt; + internal int IterationCount; + internal int? KeyLength; + internal System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn Prf; internal readonly void Encode(AsnWriter writer) { @@ -54,7 +57,7 @@ internal readonly void Encode(AsnWriter writer, Asn1Tag tag) AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER); Prf.Encode(tmp); - if (!tmp.EncodedValueEquals(DefaultPrf)) + if (!tmp.EncodedValueEquals(SharedPbkdf2Params.DefaultPrf)) { tmp.CopyTo(writer); } @@ -136,7 +139,7 @@ private static void DecodeCore(ref ValueAsnReader reader, Asn1Tag expectedTag, R } else { - defaultReader = new ValueAsnReader(DefaultPrf, AsnEncodingRules.DER); + defaultReader = new ValueAsnReader(SharedPbkdf2Params.DefaultPrf, AsnEncodingRules.DER); System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn.Decode(ref defaultReader, rebind, out decoded.Prf); } diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs12/MacData.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs12/MacData.xml.cs index 7c4e07d3702975..d6c26ba509af78 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs12/MacData.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/Pkcs12/MacData.xml.cs @@ -8,22 +8,17 @@ namespace System.Security.Cryptography.Asn1.Pkcs12 { - [StructLayout(LayoutKind.Sequential)] - internal partial struct MacData + file static class SharedMacData { - private static ReadOnlySpan DefaultIterationCount => [0x02, 0x01, 0x01]; - - internal System.Security.Cryptography.Asn1.DigestInfoAsn Mac; - internal ReadOnlyMemory MacSalt; - internal int IterationCount; + internal static ReadOnlySpan DefaultIterationCount => [0x02, 0x01, 0x01]; #if DEBUG - static MacData() + static SharedMacData() { MacData decoded = default; ValueAsnReader reader; - reader = new ValueAsnReader(DefaultIterationCount, AsnEncodingRules.DER); + reader = new ValueAsnReader(SharedMacData.DefaultIterationCount, AsnEncodingRules.DER); if (!reader.TryReadInt32(out decoded.IterationCount)) { @@ -33,6 +28,14 @@ static MacData() reader.ThrowIfNotEmpty(); } #endif + } + + [StructLayout(LayoutKind.Sequential)] + internal partial struct MacData + { + internal System.Security.Cryptography.Asn1.DigestInfoAsn Mac; + internal ReadOnlyMemory MacSalt; + internal int IterationCount; internal readonly void Encode(AsnWriter writer) { @@ -52,7 +55,7 @@ internal readonly void Encode(AsnWriter writer, Asn1Tag tag) AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER, initialCapacity: AsnManagedIntegerDerMaxEncodeSize); tmp.WriteInteger(IterationCount); - if (!tmp.EncodedValueEquals(DefaultIterationCount)) + if (!tmp.EncodedValueEquals(SharedMacData.DefaultIterationCount)) { tmp.CopyTo(writer); } @@ -131,7 +134,7 @@ private static void DecodeCore(ref ValueAsnReader reader, Asn1Tag expectedTag, R } else { - defaultReader = new ValueAsnReader(DefaultIterationCount, AsnEncodingRules.DER); + defaultReader = new ValueAsnReader(SharedMacData.DefaultIterationCount, AsnEncodingRules.DER); if (!defaultReader.TryReadInt32(out decoded.IterationCount)) { diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/PssParamsAsn.manual.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/PssParamsAsn.manual.cs index b80004f5efa50c..9dede0a3d7d312 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/PssParamsAsn.manual.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/PssParamsAsn.manual.cs @@ -61,4 +61,60 @@ internal RSASignaturePadding GetSignaturePadding( return RSASignaturePadding.Pss; } } + + internal ref partial struct ValuePssParamsAsn + { + internal RSASignaturePadding GetSignaturePadding(int? digestValueLength = null) + { + if (TrailerField != 1) + { + throw new CryptographicException(SR.Cryptography_Pkcs_InvalidSignatureParameters); + } + + if (MaskGenAlgorithm.Algorithm != Oids.Mgf1) + { + throw new CryptographicException( + SR.Cryptography_Pkcs_PssParametersMgfNotSupported, + MaskGenAlgorithm.Algorithm); + } + + if (!MaskGenAlgorithm.HasParameters) + { + throw new CryptographicException(SR.Cryptography_Pkcs_InvalidSignatureParameters); + } + + ValueAlgorithmIdentifierAsn.Decode( + MaskGenAlgorithm.Parameters, + AsnEncodingRules.DER, + out ValueAlgorithmIdentifierAsn mgfParams); + + if (mgfParams.Algorithm != HashAlgorithm.Algorithm) + { + throw new CryptographicException( + SR.Format( + SR.Cryptography_Pkcs_PssParametersMgfHashMismatch, + mgfParams.Algorithm, + HashAlgorithm.Algorithm)); + } + + int saltSize = digestValueLength.GetValueOrDefault(); + + if (!digestValueLength.HasValue) + { + saltSize = Helpers.HashOidToByteLength(HashAlgorithm.Algorithm); + } + + if (SaltLength != saltSize) + { + throw new CryptographicException( + SR.Format( + SR.Cryptography_Pkcs_PssParametersSaltMismatch, + SaltLength, + HashAlgorithm.Algorithm)); + } + + // When RSASignaturePadding supports custom salt sizes this return will look different. + return RSASignaturePadding.Pss; + } + } } diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/PssParamsAsn.xml b/src/libraries/Common/src/System/Security/Cryptography/Asn1/PssParamsAsn.xml index b80c54c49c7324..e3e3f32adf9f51 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/PssParamsAsn.xml +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/PssParamsAsn.xml @@ -2,11 +2,12 @@ + namespace="System.Security.Cryptography.Asn1" + emitType="both"> - - + + - \ No newline at end of file + diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/PssParamsAsn.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/PssParamsAsn.xml.cs index 4c952f6a3f49dc..43e8ff1f4c64ad 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/PssParamsAsn.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/PssParamsAsn.xml.cs @@ -8,38 +8,32 @@ namespace System.Security.Cryptography.Asn1 { - [StructLayout(LayoutKind.Sequential)] - internal partial struct PssParamsAsn + file static class SharedPssParamsAsn { - private static ReadOnlySpan DefaultHashAlgorithm => [0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00]; + internal static ReadOnlySpan DefaultHashAlgorithm => [0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00]; - private static ReadOnlySpan DefaultMaskGenAlgorithm => [0x30, 0x16, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x08, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00]; + internal static ReadOnlySpan DefaultMaskGenAlgorithm => [0x30, 0x16, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x08, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00]; - private static ReadOnlySpan DefaultSaltLength => [0x02, 0x01, 0x14]; + internal static ReadOnlySpan DefaultSaltLength => [0x02, 0x01, 0x14]; - private static ReadOnlySpan DefaultTrailerField => [0x02, 0x01, 0x01]; - - internal System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn HashAlgorithm; - internal System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn MaskGenAlgorithm; - internal int SaltLength; - internal int TrailerField; + internal static ReadOnlySpan DefaultTrailerField => [0x02, 0x01, 0x01]; #if DEBUG - static PssParamsAsn() + static SharedPssParamsAsn() { PssParamsAsn decoded = default; ReadOnlyMemory rebind = default; ValueAsnReader reader; - reader = new ValueAsnReader(DefaultHashAlgorithm, AsnEncodingRules.DER); + reader = new ValueAsnReader(SharedPssParamsAsn.DefaultHashAlgorithm, AsnEncodingRules.DER); System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn.Decode(ref reader, rebind, out decoded.HashAlgorithm); reader.ThrowIfNotEmpty(); - reader = new ValueAsnReader(DefaultMaskGenAlgorithm, AsnEncodingRules.DER); + reader = new ValueAsnReader(SharedPssParamsAsn.DefaultMaskGenAlgorithm, AsnEncodingRules.DER); System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn.Decode(ref reader, rebind, out decoded.MaskGenAlgorithm); reader.ThrowIfNotEmpty(); - reader = new ValueAsnReader(DefaultSaltLength, AsnEncodingRules.DER); + reader = new ValueAsnReader(SharedPssParamsAsn.DefaultSaltLength, AsnEncodingRules.DER); if (!reader.TryReadInt32(out decoded.SaltLength)) { @@ -48,7 +42,7 @@ static PssParamsAsn() reader.ThrowIfNotEmpty(); - reader = new ValueAsnReader(DefaultTrailerField, AsnEncodingRules.DER); + reader = new ValueAsnReader(SharedPssParamsAsn.DefaultTrailerField, AsnEncodingRules.DER); if (!reader.TryReadInt32(out decoded.TrailerField)) { @@ -58,6 +52,15 @@ static PssParamsAsn() reader.ThrowIfNotEmpty(); } #endif + } + + [StructLayout(LayoutKind.Sequential)] + internal partial struct PssParamsAsn + { + internal System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn HashAlgorithm; + internal System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn MaskGenAlgorithm; + internal int SaltLength; + internal int TrailerField; internal readonly void Encode(AsnWriter writer) { @@ -74,7 +77,7 @@ internal readonly void Encode(AsnWriter writer, Asn1Tag tag) AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER); HashAlgorithm.Encode(tmp); - if (!tmp.EncodedValueEquals(DefaultHashAlgorithm)) + if (!tmp.EncodedValueEquals(SharedPssParamsAsn.DefaultHashAlgorithm)) { writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); tmp.CopyTo(writer); @@ -88,7 +91,7 @@ internal readonly void Encode(AsnWriter writer, Asn1Tag tag) AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER); MaskGenAlgorithm.Encode(tmp); - if (!tmp.EncodedValueEquals(DefaultMaskGenAlgorithm)) + if (!tmp.EncodedValueEquals(SharedPssParamsAsn.DefaultMaskGenAlgorithm)) { writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); tmp.CopyTo(writer); @@ -103,7 +106,7 @@ internal readonly void Encode(AsnWriter writer, Asn1Tag tag) AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER, initialCapacity: AsnManagedIntegerDerMaxEncodeSize); tmp.WriteInteger(SaltLength); - if (!tmp.EncodedValueEquals(DefaultSaltLength)) + if (!tmp.EncodedValueEquals(SharedPssParamsAsn.DefaultSaltLength)) { writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); tmp.CopyTo(writer); @@ -118,7 +121,7 @@ internal readonly void Encode(AsnWriter writer, Asn1Tag tag) AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER, initialCapacity: AsnManagedIntegerDerMaxEncodeSize); tmp.WriteInteger(TrailerField); - if (!tmp.EncodedValueEquals(DefaultTrailerField)) + if (!tmp.EncodedValueEquals(SharedPssParamsAsn.DefaultTrailerField)) { writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); tmp.CopyTo(writer); @@ -183,7 +186,7 @@ private static void DecodeCore(ref ValueAsnReader reader, Asn1Tag expectedTag, R } else { - defaultReader = new ValueAsnReader(DefaultHashAlgorithm, AsnEncodingRules.DER); + defaultReader = new ValueAsnReader(SharedPssParamsAsn.DefaultHashAlgorithm, AsnEncodingRules.DER); System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn.Decode(ref defaultReader, rebind, out decoded.HashAlgorithm); } @@ -196,7 +199,7 @@ private static void DecodeCore(ref ValueAsnReader reader, Asn1Tag expectedTag, R } else { - defaultReader = new ValueAsnReader(DefaultMaskGenAlgorithm, AsnEncodingRules.DER); + defaultReader = new ValueAsnReader(SharedPssParamsAsn.DefaultMaskGenAlgorithm, AsnEncodingRules.DER); System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn.Decode(ref defaultReader, rebind, out decoded.MaskGenAlgorithm); } @@ -214,7 +217,136 @@ private static void DecodeCore(ref ValueAsnReader reader, Asn1Tag expectedTag, R } else { - defaultReader = new ValueAsnReader(DefaultSaltLength, AsnEncodingRules.DER); + defaultReader = new ValueAsnReader(SharedPssParamsAsn.DefaultSaltLength, AsnEncodingRules.DER); + + if (!defaultReader.TryReadInt32(out decoded.SaltLength)) + { + defaultReader.ThrowIfNotEmpty(); + } + + } + + + if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 3))) + { + explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); + + if (!explicitReader.TryReadInt32(out decoded.TrailerField)) + { + explicitReader.ThrowIfNotEmpty(); + } + + explicitReader.ThrowIfNotEmpty(); + } + else + { + defaultReader = new ValueAsnReader(SharedPssParamsAsn.DefaultTrailerField, AsnEncodingRules.DER); + + if (!defaultReader.TryReadInt32(out decoded.TrailerField)) + { + defaultReader.ThrowIfNotEmpty(); + } + + } + + + sequenceReader.ThrowIfNotEmpty(); + } + } + + [StructLayout(LayoutKind.Sequential)] + internal ref partial struct ValuePssParamsAsn + { + internal System.Security.Cryptography.Asn1.ValueAlgorithmIdentifierAsn HashAlgorithm; + internal System.Security.Cryptography.Asn1.ValueAlgorithmIdentifierAsn MaskGenAlgorithm; + internal int SaltLength; + internal int TrailerField; + + internal static void Decode(ReadOnlySpan encoded, AsnEncodingRules ruleSet, out ValuePssParamsAsn decoded) + { + Decode(Asn1Tag.Sequence, encoded, ruleSet, out decoded); + } + + internal static void Decode(Asn1Tag expectedTag, ReadOnlySpan encoded, AsnEncodingRules ruleSet, out ValuePssParamsAsn decoded) + { + try + { + ValueAsnReader reader = new ValueAsnReader(encoded, ruleSet); + + DecodeCore(ref reader, expectedTag, out decoded); + reader.ThrowIfNotEmpty(); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + internal static void Decode(scoped ref ValueAsnReader reader, out ValuePssParamsAsn decoded) + { + Decode(ref reader, Asn1Tag.Sequence, out decoded); + } + + internal static void Decode(scoped ref ValueAsnReader reader, Asn1Tag expectedTag, out ValuePssParamsAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(scoped ref ValueAsnReader reader, Asn1Tag expectedTag, out ValuePssParamsAsn decoded) + { + decoded = default; + ValueAsnReader sequenceReader = reader.ReadSequence(expectedTag); + ValueAsnReader explicitReader; + ValueAsnReader defaultReader; + + + if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 0))) + { + explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); + System.Security.Cryptography.Asn1.ValueAlgorithmIdentifierAsn.Decode(ref explicitReader, out decoded.HashAlgorithm); + explicitReader.ThrowIfNotEmpty(); + } + else + { + defaultReader = new ValueAsnReader(SharedPssParamsAsn.DefaultHashAlgorithm, AsnEncodingRules.DER); + System.Security.Cryptography.Asn1.ValueAlgorithmIdentifierAsn.Decode(ref defaultReader, out decoded.HashAlgorithm); + } + + + if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1))) + { + explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); + System.Security.Cryptography.Asn1.ValueAlgorithmIdentifierAsn.Decode(ref explicitReader, out decoded.MaskGenAlgorithm); + explicitReader.ThrowIfNotEmpty(); + } + else + { + defaultReader = new ValueAsnReader(SharedPssParamsAsn.DefaultMaskGenAlgorithm, AsnEncodingRules.DER); + System.Security.Cryptography.Asn1.ValueAlgorithmIdentifierAsn.Decode(ref defaultReader, out decoded.MaskGenAlgorithm); + } + + + if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 2))) + { + explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); + + if (!explicitReader.TryReadInt32(out decoded.SaltLength)) + { + explicitReader.ThrowIfNotEmpty(); + } + + explicitReader.ThrowIfNotEmpty(); + } + else + { + defaultReader = new ValueAsnReader(SharedPssParamsAsn.DefaultSaltLength, AsnEncodingRules.DER); if (!defaultReader.TryReadInt32(out decoded.SaltLength)) { @@ -237,7 +369,7 @@ private static void DecodeCore(ref ValueAsnReader reader, Asn1Tag expectedTag, R } else { - defaultReader = new ValueAsnReader(DefaultTrailerField, AsnEncodingRules.DER); + defaultReader = new ValueAsnReader(SharedPssParamsAsn.DefaultTrailerField, AsnEncodingRules.DER); if (!defaultReader.TryReadInt32(out decoded.TrailerField)) { diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/SubjectPublicKeyInfoAsn.xml b/src/libraries/Common/src/System/Security/Cryptography/Asn1/SubjectPublicKeyInfoAsn.xml index 1fa5606465e4ca..8f749cb9c0a6f0 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/SubjectPublicKeyInfoAsn.xml +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/SubjectPublicKeyInfoAsn.xml @@ -2,7 +2,8 @@ + namespace="System.Security.Cryptography.Asn1" + emitType="both"> - + - \ No newline at end of file + diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/SubjectPublicKeyInfoAsn.xml.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1/SubjectPublicKeyInfoAsn.xml.cs index b738b0690a8a06..80ce6bb17b408e 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/SubjectPublicKeyInfoAsn.xml.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/SubjectPublicKeyInfoAsn.xml.cs @@ -86,6 +86,71 @@ private static void DecodeCore(ref ValueAsnReader reader, Asn1Tag expectedTag, R } + sequenceReader.ThrowIfNotEmpty(); + } + } + + [StructLayout(LayoutKind.Sequential)] + internal ref partial struct ValueSubjectPublicKeyInfoAsn + { + internal System.Security.Cryptography.Asn1.ValueAlgorithmIdentifierAsn Algorithm; + internal ReadOnlySpan SubjectPublicKey; + + internal static void Decode(ReadOnlySpan encoded, AsnEncodingRules ruleSet, out ValueSubjectPublicKeyInfoAsn decoded) + { + Decode(Asn1Tag.Sequence, encoded, ruleSet, out decoded); + } + + internal static void Decode(Asn1Tag expectedTag, ReadOnlySpan encoded, AsnEncodingRules ruleSet, out ValueSubjectPublicKeyInfoAsn decoded) + { + try + { + ValueAsnReader reader = new ValueAsnReader(encoded, ruleSet); + + DecodeCore(ref reader, expectedTag, out decoded); + reader.ThrowIfNotEmpty(); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + internal static void Decode(scoped ref ValueAsnReader reader, out ValueSubjectPublicKeyInfoAsn decoded) + { + Decode(ref reader, Asn1Tag.Sequence, out decoded); + } + + internal static void Decode(scoped ref ValueAsnReader reader, Asn1Tag expectedTag, out ValueSubjectPublicKeyInfoAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(scoped ref ValueAsnReader reader, Asn1Tag expectedTag, out ValueSubjectPublicKeyInfoAsn decoded) + { + decoded = default; + ValueAsnReader sequenceReader = reader.ReadSequence(expectedTag); + ReadOnlySpan tmpSpan; + + System.Security.Cryptography.Asn1.ValueAlgorithmIdentifierAsn.Decode(ref sequenceReader, out decoded.Algorithm); + + if (sequenceReader.TryReadPrimitiveBitString(out _, out tmpSpan)) + { + decoded.SubjectPublicKey = tmpSpan; + } + else + { + decoded.SubjectPublicKey = sequenceReader.ReadBitString(out _); + } + + sequenceReader.ThrowIfNotEmpty(); } } diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/X509ExtensionAsn.xml b/src/libraries/Common/src/System/Security/Cryptography/Asn1/X509ExtensionAsn.xml index 0bf07c6d172380..457a2864dd625a 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/X509ExtensionAsn.xml +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/X509ExtensionAsn.xml @@ -2,7 +2,8 @@ + namespace="System.Security.Cryptography.Asn1" + emitType="both"> + + + Error, unknown ValueFieldDef node [] + + + + + + + + + + + + + + + + + internal ReadOnlySpan<byte> ; + internal bool Has; + + + internal ReadOnlySpan<byte> ; + internal bool Has; + + + internal ReadOnlySpan<byte> ; + internal bool Has; + + + internal ReadOnlySpan<byte> ; + internal bool Has; + + + internal ReadOnlySpan<byte> ; + internal bool Has; + + + + internal ; + internal bool Has; + + + internal ? ; + + + internal ReadOnlySpan<byte> ; + internal bool Has; + + + + + + + + + + + + + + + + + + + tmp; + .Decode(ref , out tmp); + = tmp; + + + .Decode(ref , out ); + + + + + + + + + + tmp; + .Decode(ref , out tmp); + = tmp; + + + .Decode(ref , out ); + + + + + + + + + = .ReadEncodedValue(); + + + + + + + + + if (!.PeekTag().HasSameClassAndValue()) + { + throw new CryptographicException(); + } + + = .ReadEncodedValue(); + + = .ReadEncodedValue(); + + + + + + + + + + = .ReadIntegerBytes(); + + + + + + + + + + if (.TryReadPrimitiveBitString(out _, out tmpSpan)) + { + = tmpSpan; + } + else + { + = .ReadBitString(out _); + } + + + + + + + + + + + if (.TryReadPrimitiveOctetString(out tmpSpan)) + { + = tmpSpan; + } + else + { + = .ReadOctetString(); + } + + + + + + + + + + + + Asn1Tag.SetOf + Asn1Tag.Sequence + + + + + if (!.PeekTag().HasSameClassAndValue()) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + + + decoded. = .ReadEncodedValue(); + + + + + + + + + decoded.Has = true; + + + + + + + + else if (tag.HasSameClassAndValue()) + { + explicitReader = reader.ReadSequence(); + explicitReader.ThrowIfNotEmpty(); + } + + else if (tag.HasSameClassAndValue()) + { + } + + + + + + + if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue()) + { + explicitReader = sequenceReader.ReadSequence(); + explicitReader.ThrowIfNotEmpty(); + } + + + + if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue()) + { + } + + + + if (sequenceReader.HasData) + { + } + + + + if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue()) + { + } + + + + + + explicitReader = sequenceReader.ReadSequence(); + explicitReader.ThrowIfNotEmpty(); + + + + + + + + + + + + + + + + else + { + defaultReader = new ValueAsnReader(, AsnEncodingRules.DER); + } + + + + + 1 + 0 + + + + + + + + + SetOf + Sequence + + + + + + ReadOnlySpan<byte> + + + + + internal Enumerable (AsnEncodingRules ruleSet) + { + return new Enumerable(, ruleSet); + } + + internal readonly ref struct Enumerable + { + private readonly ReadOnlySpan<byte> _encoded; + private readonly AsnEncodingRules _ruleSet; + + internal Enumerable(ReadOnlySpan<byte> encoded, AsnEncodingRules ruleSet) + { + _encoded = encoded; + _ruleSet = ruleSet; + } + + public Enumerator GetEnumerator() => new Enumerator(_encoded, _ruleSet); + + internal ref struct Enumerator + { + private ValueAsnReader _reader; + private _current; + + internal Enumerator(ReadOnlySpan<byte> encoded, AsnEncodingRules ruleSet) + { + if (!encoded.IsEmpty) + { + ValueAsnReader outerReader = new ValueAsnReader(encoded, ruleSet); + _reader = outerReader.Read(); + outerReader.ThrowIfNotEmpty(); + } + + _current = default; + } + + public Current => _current; + + public bool MoveNext() + { + if (!_reader.HasData) + { + return false; + } + + .Decode(ref _reader, out _current); + _current = _reader.ReadEncodedValue(); + return true; + } + } + } + + + + + + + + + diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnValueReader.cs b/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnValueReader.cs deleted file mode 100644 index ea9c1c3c7cadae..00000000000000 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/AsnValueReader.cs +++ /dev/null @@ -1,258 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Numerics; -using System.Security.Cryptography; - -namespace System.Formats.Asn1 -{ - internal ref struct AsnValueReader - { - private static readonly byte[] s_singleByte = new byte[1]; - - private ReadOnlySpan _span; - private readonly AsnEncodingRules _ruleSet; - - internal AsnValueReader(ReadOnlySpan span, AsnEncodingRules ruleSet) - { - _span = span; - _ruleSet = ruleSet; - } - - internal bool HasData => !_span.IsEmpty; - - internal void ThrowIfNotEmpty() - { - if (!_span.IsEmpty) - { - new AsnReader(s_singleByte, _ruleSet).ThrowIfNotEmpty(); - } - } - - internal Asn1Tag PeekTag() - { - return Asn1Tag.Decode(_span, out _); - } - - internal ReadOnlySpan PeekContentBytes() - { - AsnDecoder.ReadEncodedValue( - _span, - _ruleSet, - out int contentOffset, - out int contentLength, - out _); - - return _span.Slice(contentOffset, contentLength); - } - - internal ReadOnlySpan PeekEncodedValue() - { - AsnDecoder.ReadEncodedValue(_span, _ruleSet, out _, out _, out int consumed); - return _span.Slice(0, consumed); - } - - internal ReadOnlySpan ReadEncodedValue() - { - ReadOnlySpan value = PeekEncodedValue(); - _span = _span.Slice(value.Length); - return value; - } - - internal bool ReadBoolean(Asn1Tag? expectedTag = default) - { - bool ret = AsnDecoder.ReadBoolean(_span, _ruleSet, out int consumed, expectedTag); - _span = _span.Slice(consumed); - return ret; - } - - internal BigInteger ReadInteger(Asn1Tag? expectedTag = default) - { - BigInteger ret = AsnDecoder.ReadInteger(_span, _ruleSet, out int consumed, expectedTag); - _span = _span.Slice(consumed); - return ret; - } - - internal bool TryReadInt32(out int value, Asn1Tag? expectedTag = default) - { - bool ret = AsnDecoder.TryReadInt32(_span, _ruleSet, out value, out int consumed, expectedTag); - _span = _span.Slice(consumed); - return ret; - } - - internal ReadOnlySpan ReadIntegerBytes(Asn1Tag? expectedTag = default) - { - ReadOnlySpan ret = AsnDecoder.ReadIntegerBytes(_span, _ruleSet, out int consumed, expectedTag); - _span = _span.Slice(consumed); - return ret; - } - - internal bool TryReadPrimitiveBitString( - out int unusedBitCount, - out ReadOnlySpan value, - Asn1Tag? expectedTag = default) - { - bool ret = AsnDecoder.TryReadPrimitiveBitString( - _span, - _ruleSet, - out unusedBitCount, - out value, - out int consumed, - expectedTag); - - _span = _span.Slice(consumed); - return ret; - } - - internal byte[] ReadBitString(out int unusedBitCount, Asn1Tag? expectedTag = default) - { - byte[] ret = AsnDecoder.ReadBitString( - _span, - _ruleSet, - out unusedBitCount, - out int consumed, - expectedTag); - - _span = _span.Slice(consumed); - return ret; - } - - internal TFlagsEnum ReadNamedBitListValue(Asn1Tag? expectedTag = default) where TFlagsEnum : Enum - { - TFlagsEnum ret = AsnDecoder.ReadNamedBitListValue(_span, _ruleSet, out int consumed, expectedTag); - _span = _span.Slice(consumed); - return ret; - } - - internal bool TryReadPrimitiveOctetString( - out ReadOnlySpan value, - Asn1Tag? expectedTag = default) - { - bool ret = AsnDecoder.TryReadPrimitiveOctetString( - _span, - _ruleSet, - out value, - out int consumed, - expectedTag); - - _span = _span.Slice(consumed); - return ret; - } - - internal byte[] ReadOctetString(Asn1Tag? expectedTag = default) - { - byte[] ret = AsnDecoder.ReadOctetString( - _span, - _ruleSet, - out int consumed, - expectedTag); - - _span = _span.Slice(consumed); - return ret; - } - - internal string ReadObjectIdentifier(Asn1Tag? expectedTag = default) - { - string ret = AsnDecoder.ReadObjectIdentifier(_span, _ruleSet, out int consumed, expectedTag); - _span = _span.Slice(consumed); - return ret; - } - - internal AsnValueReader ReadSequence(Asn1Tag? expectedTag = default) - { - AsnDecoder.ReadSequence( - _span, - _ruleSet, - out int contentOffset, - out int contentLength, - out int bytesConsumed, - expectedTag); - - ReadOnlySpan content = _span.Slice(contentOffset, contentLength); - _span = _span.Slice(bytesConsumed); - return new AsnValueReader(content, _ruleSet); - } - - internal AsnValueReader ReadSetOf(Asn1Tag? expectedTag = default, bool skipSortOrderValidation = false) - { - AsnDecoder.ReadSetOf( - _span, - _ruleSet, - out int contentOffset, - out int contentLength, - out int bytesConsumed, - skipSortOrderValidation: skipSortOrderValidation, - expectedTag: expectedTag); - - ReadOnlySpan content = _span.Slice(contentOffset, contentLength); - _span = _span.Slice(bytesConsumed); - return new AsnValueReader(content, _ruleSet); - } - - internal DateTimeOffset ReadUtcTime(Asn1Tag? expectedTag = default) - { - DateTimeOffset ret = AsnDecoder.ReadUtcTime(_span, _ruleSet, out int consumed, expectedTag: expectedTag); - _span = _span.Slice(consumed); - return ret; - } - - internal DateTimeOffset ReadGeneralizedTime(Asn1Tag? expectedTag = default) - { - DateTimeOffset ret = AsnDecoder.ReadGeneralizedTime(_span, _ruleSet, out int consumed, expectedTag); - _span = _span.Slice(consumed); - return ret; - } - - internal string ReadCharacterString(UniversalTagNumber encodingType, Asn1Tag? expectedTag = default) - { - string ret = AsnDecoder.ReadCharacterString(_span, _ruleSet, encodingType, out int consumed, expectedTag); - _span = _span.Slice(consumed); - return ret; - } - - internal TEnum ReadEnumeratedValue(Asn1Tag? expectedTag = null) where TEnum : Enum - { - TEnum ret = AsnDecoder.ReadEnumeratedValue(_span, _ruleSet, out int consumed, expectedTag); - _span = _span.Slice(consumed); - return ret; - } - } - - internal static class AsnWriterExtensions - { - internal static void WriteEncodedValueForCrypto( - this AsnWriter writer, - ReadOnlySpan value) - { - try - { - writer.WriteEncodedValue(value); - } - catch (ArgumentException e) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); - } - } - - internal static void WriteObjectIdentifierForCrypto( - this AsnWriter writer, - string value) - { - try - { - writer.WriteObjectIdentifier(value); - } - catch (ArgumentException e) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); - } - } - - internal static ArraySegment RentAndEncode(this AsnWriter writer) - { - byte[] rented = CryptoPool.Rent(writer.GetEncodedLength()); - int written = writer.Encode(rented); - return new ArraySegment(rented, 0, written); - } - } -} diff --git a/src/libraries/Common/src/System/Security/Cryptography/AsnWriter/AsnWriterExtensions.cs b/src/libraries/Common/src/System/Security/Cryptography/AsnWriter/AsnWriterExtensions.cs new file mode 100644 index 00000000000000..24e659930319af --- /dev/null +++ b/src/libraries/Common/src/System/Security/Cryptography/AsnWriter/AsnWriterExtensions.cs @@ -0,0 +1,38 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Security.Cryptography; + +namespace System.Formats.Asn1 +{ + internal static class AsnWriterExtensions + { + internal static void WriteEncodedValueForCrypto( + this AsnWriter writer, + ReadOnlySpan value) + { + try + { + writer.WriteEncodedValue(value); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + internal static void WriteObjectIdentifierForCrypto( + this AsnWriter writer, + string value) + { + try + { + writer.WriteObjectIdentifier(value); + } + catch (ArgumentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + } +} diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/System.Security.Cryptography.Asn1Reader.Shared.projitems b/src/libraries/Common/src/System/Security/Cryptography/AsnWriter/System.Security.Cryptography.AsnWriter.Shared.projitems similarity index 66% rename from src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/System.Security.Cryptography.Asn1Reader.Shared.projitems rename to src/libraries/Common/src/System/Security/Cryptography/AsnWriter/System.Security.Cryptography.AsnWriter.Shared.projitems index 518a8c66f4b6b8..2ebe3a4c150ab7 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1Reader/System.Security.Cryptography.Asn1Reader.Shared.projitems +++ b/src/libraries/Common/src/System/Security/Cryptography/AsnWriter/System.Security.Cryptography.AsnWriter.Shared.projitems @@ -9,8 +9,8 @@ - - Common\System\Security\Cryptography\Asn1Reader\AsnValueReader.cs + + Common\System\Security\Cryptography\AsnWriter\AsnWriterExtensions.cs diff --git a/src/libraries/Common/src/System/Security/Cryptography/RSAOpenSsl.cs b/src/libraries/Common/src/System/Security/Cryptography/RSAOpenSsl.cs index 331594af026fc5..4c69763709e631 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/RSAOpenSsl.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/RSAOpenSsl.cs @@ -453,30 +453,18 @@ public override void ImportParameters(RSAParameters parameters) if (parameters.D != null) { AsnWriter writer = RSAKeyFormatHelper.WritePkcs8PrivateKey(parameters); - ArraySegment pkcs8 = writer.RentAndEncode(); - - try - { - ImportPkcs8PrivateKey(pkcs8, checkAlgorithm: false, out _); - } - finally + writer.Encode(this, static (RSAOpenSsl rsa, ReadOnlySpan pkcs8) => { - CryptoPool.Return(pkcs8); - } + rsa.ImportPkcs8PrivateKey(pkcs8, checkAlgorithm: false, out _); + }); } else { AsnWriter writer = RSAKeyFormatHelper.WriteSubjectPublicKeyInfo(parameters); - ArraySegment spki = writer.RentAndEncode(); - - try + writer.Encode(this, static (RSAOpenSsl rsa, ReadOnlySpan spki) => { - ImportSubjectPublicKeyInfo(spki, checkAlgorithm: false, out _); - } - finally - { - CryptoPool.Return(spki); - } + rsa.ImportSubjectPublicKeyInfo(spki, checkAlgorithm: false, out _); + }); } } @@ -501,16 +489,10 @@ public override void ImportRSAPublicKey(ReadOnlySpan source, out int bytes } AsnWriter writer = RSAKeyFormatHelper.WriteSubjectPublicKeyInfo(source.Slice(0, read)); - ArraySegment spki = writer.RentAndEncode(); - - try - { - ImportSubjectPublicKeyInfo(spki, checkAlgorithm: false, out _); - } - finally + writer.Encode(this, static (RSAOpenSsl rsa, ReadOnlySpan spki) => { - CryptoPool.Return(spki); - } + rsa.ImportSubjectPublicKeyInfo(spki, checkAlgorithm: false, out _); + }); bytesRead = read; } @@ -617,16 +599,11 @@ public override void ImportRSAPrivateKey(ReadOnlySpan source, out int byte } AsnWriter writer = RSAKeyFormatHelper.WritePkcs8PrivateKey(source.Slice(0, read)); - ArraySegment pkcs8 = writer.RentAndEncode(); - try + writer.Encode(this, static (RSAOpenSsl rsa, ReadOnlySpan pkcs8) => { - ImportPkcs8PrivateKey(pkcs8, checkAlgorithm: false, out _); - } - finally - { - CryptoPool.Return(pkcs8); - } + rsa.ImportPkcs8PrivateKey(pkcs8, checkAlgorithm: false, out _); + }); bytesRead = read; } diff --git a/src/libraries/Common/tests/System/Collections/ICollectionTest.cs b/src/libraries/Common/tests/System/Collections/ICollectionTest.cs index aad8573b93320a..1dc5cd12964230 100644 --- a/src/libraries/Common/tests/System/Collections/ICollectionTest.cs +++ b/src/libraries/Common/tests/System/Collections/ICollectionTest.cs @@ -200,7 +200,7 @@ protected void AssertThrows( if (exception == null) { throw EqualException.ForMismatchedValues( - exceptionTypes, + string.Join(", ", exceptionTypes.Select(t => t.ToString())), null, "Expected an exception but got null."); } @@ -208,8 +208,8 @@ protected void AssertThrows( if (!exceptionTypes.Contains(exceptionType)) { throw EqualException.ForMismatchedValues( - exceptionTypes, - exceptionType, + string.Join(", ", exceptionTypes.Select(t => t.ToString())), + exceptionType.ToString(), "Caught wrong exception."); } } diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/CompositeMLDsa/CompositeMLDsaTestHelpers.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/CompositeMLDsa/CompositeMLDsaTestHelpers.cs index 1a1622f3a2bc8c..3ccadf5a25a89e 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/CompositeMLDsa/CompositeMLDsaTestHelpers.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/CompositeMLDsa/CompositeMLDsaTestHelpers.cs @@ -563,8 +563,8 @@ private static RSAParameters RSAParametersFromRawPrivateKey(ReadOnlySpan k { RSAParameters parameters = default; - AsnValueReader reader = new AsnValueReader(key, AsnEncodingRules.BER); - AsnValueReader sequenceReader = reader.ReadSequence(Asn1Tag.Sequence); + ValueAsnReader reader = new ValueAsnReader(key, AsnEncodingRules.BER); + ValueAsnReader sequenceReader = reader.ReadSequence(Asn1Tag.Sequence); if (!sequenceReader.TryReadInt32(out int version)) { diff --git a/src/libraries/Microsoft.Bcl.Cryptography/src/Microsoft.Bcl.Cryptography.csproj b/src/libraries/Microsoft.Bcl.Cryptography/src/Microsoft.Bcl.Cryptography.csproj index aeae1c55ea7cc0..6670adf751a160 100644 --- a/src/libraries/Microsoft.Bcl.Cryptography/src/Microsoft.Bcl.Cryptography.csproj +++ b/src/libraries/Microsoft.Bcl.Cryptography/src/Microsoft.Bcl.Cryptography.csproj @@ -21,7 +21,7 @@ - + - + ? Diagnostics { get; private set; } + public List? Diagnostics { get; private set; } public SourceGenerationSpec? GetSourceGenerationSpec(ImmutableArray invocations, CancellationToken cancellationToken) { if (!_langVersionIsSupported) { - RecordDiagnostic(DiagnosticDescriptors.LanguageVersionNotSupported, trimmedLocation: Location.None); + RecordDiagnostic(DiagnosticDescriptors.LanguageVersionNotSupported, location: Location.None); return null; } @@ -979,10 +979,10 @@ private void ReportContainingTypeDiagnosticIfRequired(TypeParseInfo typeParseInf } } - private void RecordDiagnostic(DiagnosticDescriptor descriptor, Location trimmedLocation, params object?[]? messageArgs) + private void RecordDiagnostic(DiagnosticDescriptor descriptor, Location location, params object?[]? messageArgs) { - Diagnostics ??= new List(); - Diagnostics.Add(DiagnosticInfo.Create(descriptor, trimmedLocation, messageArgs)); + Diagnostics ??= new List(); + Diagnostics.Add(Diagnostic.Create(descriptor, location, messageArgs)); } private void CheckIfToEmitParseEnumMethod() diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs index 4a3d5bbf7dea81..816bdff43f5c40 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs @@ -6,10 +6,10 @@ using System.Diagnostics; using System.Reflection; using System.Threading; +using System.Collections.Immutable; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using SourceGenerators; namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { @@ -37,7 +37,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) ? new CompilationData((CSharpCompilation)compilation) : null); - IncrementalValueProvider<(SourceGenerationSpec?, ImmutableEquatableArray?)> genSpec = context.SyntaxProvider + IncrementalValueProvider<(SourceGenerationSpec?, ImmutableArray)> genSpec = context.SyntaxProvider .CreateSyntaxProvider( (node, _) => BinderInvocation.IsCandidateSyntaxNode(node), BinderInvocation.Create) @@ -48,14 +48,16 @@ public void Initialize(IncrementalGeneratorInitializationContext context) { if (tuple.Right is not CompilationData compilationData) { - return (null, null); + return (null, ImmutableArray.Empty); } try { Parser parser = new(compilationData); SourceGenerationSpec? spec = parser.GetSourceGenerationSpec(tuple.Left, cancellationToken); - ImmutableEquatableArray? diagnostics = parser.Diagnostics?.ToImmutableEquatableArray(); + ImmutableArray diagnostics = parser.Diagnostics is { } diags + ? diags.ToImmutableArray() + : ImmutableArray.Empty; return (spec, diagnostics); } catch (Exception ex) @@ -65,7 +67,26 @@ public void Initialize(IncrementalGeneratorInitializationContext context) }) .WithTrackingName(GenSpecTrackingName); - context.RegisterSourceOutput(genSpec, ReportDiagnosticsAndEmitSource); + // Project the combined pipeline result to just the equatable model, discarding diagnostics. + // SourceGenerationSpec implements value equality, so Roslyn's Select operator will compare + // successive model snapshots and only propagate changes downstream when the model structurally + // differs. This ensures source generation is fully incremental: re-emitting code only when + // the binding spec actually changes, not on every keystroke or positional shift. + IncrementalValueProvider sourceGenerationSpec = + genSpec.Select(static (t, _) => t.Item1); + + context.RegisterSourceOutput(sourceGenerationSpec, EmitSource); + + // Project to just the diagnostics, discarding the model. ImmutableArray does not + // implement value equality, so Roslyn's incremental pipeline uses reference equality for these + // values — the callback fires on every compilation change. This is by design: diagnostic + // emission is cheap, and we need fresh SourceLocation instances that are pragma-suppressible + // (cf. https://github.com/dotnet/runtime/issues/92509). + // No source code is generated from this pipeline — it exists solely to report diagnostics. + IncrementalValueProvider> diagnostics = + genSpec.Select(static (t, _) => t.Item2); + + context.RegisterSourceOutput(diagnostics, EmitDiagnostics); if (!s_hasInitializedInterceptorVersion) { @@ -136,17 +157,17 @@ internal static int DetermineInterceptableVersion() /// public Action? OnSourceEmitting { get; init; } - private void ReportDiagnosticsAndEmitSource(SourceProductionContext sourceProductionContext, (SourceGenerationSpec? SourceGenerationSpec, ImmutableEquatableArray? Diagnostics) input) + private static void EmitDiagnostics(SourceProductionContext context, ImmutableArray diagnostics) { - if (input.Diagnostics is ImmutableEquatableArray diagnostics) + foreach (Diagnostic diagnostic in diagnostics) { - foreach (DiagnosticInfo diagnostic in diagnostics) - { - sourceProductionContext.ReportDiagnostic(diagnostic.CreateDiagnostic()); - } + context.ReportDiagnostic(diagnostic); } + } - if (input.SourceGenerationSpec is SourceGenerationSpec spec) + private void EmitSource(SourceProductionContext sourceProductionContext, SourceGenerationSpec? spec) + { + if (spec is not null) { OnSourceEmitting?.Invoke(spec); Emitter emitter = new(spec); diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Microsoft.Extensions.Configuration.Binder.SourceGeneration.csproj b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Microsoft.Extensions.Configuration.Binder.SourceGeneration.csproj index bf12a1fc225b81..a0fac6dbbfb626 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Microsoft.Extensions.Configuration.Binder.SourceGeneration.csproj +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Microsoft.Extensions.Configuration.Binder.SourceGeneration.csproj @@ -30,7 +30,6 @@ - diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/GeneratorTests.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/GeneratorTests.cs index 7eee48fdeb28ca..e15f9625a971a1 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/GeneratorTests.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/GeneratorTests.cs @@ -12,6 +12,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; @@ -414,5 +415,129 @@ public class AnotherGraphWithUnsupportedMembers Assert.True(result.Diagnostics.Any(diag => diag.Id == Diagnostics.TypeNotSupported.Id)); Assert.True(result.Diagnostics.Any(diag => diag.Id == Diagnostics.PropertyNotSupported.Id)); } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNetCore))] + public async Task Diagnostic_HasPragmaSuppressibleLocation() + { + // SYSLIB1103: ValueTypesInvalidForBind (Warning, configurable). + string source = """ + #pragma warning disable SYSLIB1103 + using System; + using Microsoft.Extensions.Configuration; + + public class Program + { + public static void Main() + { + ConfigurationBuilder configurationBuilder = new(); + IConfigurationRoot config = configurationBuilder.Build(); + + int myInt = 1; + config.Bind(myInt); + } + } + """; + + ConfigBindingGenRunResult result = await RunGeneratorAndUpdateCompilation(source); + var effective = CompilationWithAnalyzers.GetEffectiveDiagnostics(result.Diagnostics, result.OutputCompilation); + Diagnostic diagnostic = Assert.Single(effective, d => d.Id == "SYSLIB1103"); + Assert.True(diagnostic.IsSuppressed); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNetCore))] + public async Task Diagnostic_NoPragma_IsNotSuppressed() + { + string source = """ + using System; + using Microsoft.Extensions.Configuration; + + public class Program + { + public static void Main() + { + ConfigurationBuilder configurationBuilder = new(); + IConfigurationRoot config = configurationBuilder.Build(); + + int myInt = 1; + config.Bind(myInt); + } + } + """; + + ConfigBindingGenRunResult result = await RunGeneratorAndUpdateCompilation(source); + var effective = CompilationWithAnalyzers.GetEffectiveDiagnostics(result.Diagnostics, result.OutputCompilation); + Diagnostic diagnostic = Assert.Single(effective, d => d.Id == "SYSLIB1103"); + Assert.False(diagnostic.IsSuppressed); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNetCore))] + public async Task Diagnostic_MultipleDiagnostics_OnlySomeSuppressed() + { + string source = """ + using System; + using System.Collections.Immutable; + using System.Text; + using System.Text.Json; + using Microsoft.Extensions.Configuration; + + public class Program + { + public static void Main() + { + ConfigurationBuilder configurationBuilder = new(); + IConfigurationRoot config = configurationBuilder.Build(); + + // SYSLIB1103 suppressed for this call only. + #pragma warning disable SYSLIB1103 + int myInt = 1; + config.Bind(myInt); + #pragma warning restore SYSLIB1103 + + // SYSLIB1103 NOT suppressed for this call. + long myLong = 1; + config.Bind(myLong); + } + } + """; + + ConfigBindingGenRunResult result = await RunGeneratorAndUpdateCompilation(source); + var effective = CompilationWithAnalyzers.GetEffectiveDiagnostics(result.Diagnostics, result.OutputCompilation) + .Where(d => d.Id == "SYSLIB1103") + .ToList(); + + Assert.Equal(2, effective.Count); + Assert.Single(effective, d => d.IsSuppressed); + Assert.Single(effective, d => !d.IsSuppressed); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNetCore))] + public async Task Diagnostic_PragmaRestoreOutsideSpan_IsNotSuppressed() + { + string source = """ + using System; + using Microsoft.Extensions.Configuration; + + public class Program + { + public static void Main() + { + ConfigurationBuilder configurationBuilder = new(); + IConfigurationRoot config = configurationBuilder.Build(); + + // Suppress and restore BEFORE the diagnostic site. + #pragma warning disable SYSLIB1103 + #pragma warning restore SYSLIB1103 + + int myInt = 1; + config.Bind(myInt); + } + } + """; + + ConfigBindingGenRunResult result = await RunGeneratorAndUpdateCompilation(source); + var effective = CompilationWithAnalyzers.GetEffectiveDiagnostics(result.Diagnostics, result.OutputCompilation); + Diagnostic diagnostic = Assert.Single(effective, d => d.Id == "SYSLIB1103"); + Assert.False(diagnostic.IsSuppressed); + } } } diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Parser.cs index c69c2db2c07e85..b98abaf7d04c99 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Parser.cs @@ -38,7 +38,7 @@ internal sealed class Parser private readonly INamedTypeSymbol _stringSymbol; private readonly Action? _reportDiagnostic; - public List Diagnostics { get; } = new(); + public List Diagnostics { get; } = new(); public Parser( INamedTypeSymbol loggerMessageAttribute, @@ -811,12 +811,14 @@ private static string GenerateClassName(TypeDeclarationSyntax typeDeclaration) private void Diag(DiagnosticDescriptor desc, Location? location, params object?[]? messageArgs) { + Diagnostic diagnostic = Diagnostic.Create(desc, location, messageArgs); + // Report immediately if callback is provided (preserves pragma suppression with original locations) - _reportDiagnostic?.Invoke(Diagnostic.Create(desc, location, messageArgs)); + _reportDiagnostic?.Invoke(diagnostic); // Also collect for scenarios that need the diagnostics list; in Roslyn 4.0+ incremental generators, - // this list is exposed via parser.Diagnostics (as ImmutableEquatableArray) and reported in Execute. - Diagnostics.Add(DiagnosticInfo.Create(desc, location, messageArgs)); + // this list is exposed via parser.Diagnostics and reported in the diagnostic pipeline. + Diagnostics.Add(diagnostic); } private static bool IsBaseOrIdentity(ITypeSymbol source, ITypeSymbol dest, Compilation compilation) diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Roslyn4.0.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Roslyn4.0.cs index ef9fa6582b53b3..9a205f873a6d00 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Roslyn4.0.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Roslyn4.0.cs @@ -25,7 +25,7 @@ public static class StepNames public void Initialize(IncrementalGeneratorInitializationContext context) { - IncrementalValuesProvider<(LoggerClassSpec? LoggerClassSpec, ImmutableEquatableArray Diagnostics, bool HasStringCreate)> loggerClasses = context.SyntaxProvider + IncrementalValuesProvider<(LoggerClassSpec? LoggerClassSpec, ImmutableArray Diagnostics, bool HasStringCreate)> loggerClasses = context.SyntaxProvider .ForAttributeWithMetadataName( #if !ROSLYN4_4_OR_GREATER context, @@ -66,7 +66,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) if (exceptionSymbol == null) { - var diagnostics = new[] { DiagnosticInfo.Create(DiagnosticDescriptors.MissingRequiredType, null, new object?[] { "System.Exception" }) }.ToImmutableEquatableArray(); + var diagnostics = ImmutableArray.Create(Diagnostic.Create(DiagnosticDescriptors.MissingRequiredType, null, new object?[] { "System.Exception" })); return (null, diagnostics, false); } @@ -92,75 +92,110 @@ public void Initialize(IncrementalGeneratorInitializationContext context) // Convert to immutable spec for incremental caching LoggerClassSpec? loggerClassSpec = logClasses.Count > 0 ? logClasses[0].ToSpec() : null; - return (loggerClassSpec, parser.Diagnostics.ToImmutableEquatableArray(), hasStringCreate); + return (loggerClassSpec, parser.Diagnostics.ToImmutableArray(), hasStringCreate); }) #if ROSLYN4_4_OR_GREATER .WithTrackingName(StepNames.LoggerMessageTransform) #endif ; - context.RegisterSourceOutput(loggerClasses.Collect(), static (spc, items) => Execute(items, spc)); + // Single collect for all per-method results, then aggregate into an equatable source + // model (using ImmutableEquatableArray for deep value equality) plus flat diagnostics. + // Diagnostics are deduplicated here because each attributed method triggers parsing of + // the entire class, producing duplicate diagnostics. + IncrementalValueProvider<(ImmutableEquatableArray<(LoggerClassSpec LoggerClassSpec, bool HasStringCreate)> Specs, ImmutableArray Diagnostics)> collected = + loggerClasses.Collect().Select(static (items, _) => + { + ImmutableArray<(LoggerClassSpec, bool)>.Builder? specs = null; + ImmutableArray.Builder? diagnostics = null; + HashSet<(string Id, TextSpan? Span, string? FilePath, string Message)>? seen = null; + + foreach (var item in items) + { + if (item.LoggerClassSpec is not null) + { + (specs ??= ImmutableArray.CreateBuilder<(LoggerClassSpec, bool)>()).Add((item.LoggerClassSpec, item.HasStringCreate)); + } + foreach (Diagnostic diagnostic in item.Diagnostics) + { + if ((seen ??= new()).Add((diagnostic.Id, diagnostic.Location?.SourceSpan, diagnostic.Location?.SourceTree?.FilePath, diagnostic.GetMessage()))) + { + (diagnostics ??= ImmutableArray.CreateBuilder()).Add(diagnostic); + } + } + } + + return ( + specs?.ToImmutableEquatableArray() ?? ImmutableEquatableArray<(LoggerClassSpec, bool)>.Empty, + diagnostics?.ToImmutable() ?? ImmutableArray.Empty); + }); + + // Project to just the equatable source model, discarding diagnostics. + // ImmutableEquatableArray provides deep value equality, so Roslyn's Select operator + // compares successive model snapshots and only propagates changes downstream when the + // model structurally differs. This ensures source generation is fully incremental. + IncrementalValueProvider> sourceGenerationSpecs = + collected.Select(static (t, _) => t.Specs); + + context.RegisterSourceOutput(sourceGenerationSpecs, static (spc, items) => EmitSource(items, spc)); + + // Project to just the diagnostics, discarding the model. ImmutableArray does not + // implement value equality, so Roslyn's incremental pipeline uses reference equality for these + // values — the callback fires on every compilation change. This is by design: diagnostic + // emission is cheap, and we need fresh SourceLocation instances that are pragma-suppressible + // (cf. https://github.com/dotnet/runtime/issues/92509). + IncrementalValueProvider> diagnosticResults = + collected.Select(static (t, _) => t.Diagnostics); + + context.RegisterSourceOutput(diagnosticResults, EmitDiagnostics); + } + + private static void EmitDiagnostics(SourceProductionContext context, ImmutableArray diagnostics) + { + foreach (Diagnostic diagnostic in diagnostics) + { + context.ReportDiagnostic(diagnostic); + } } - private static void Execute(ImmutableArray<(LoggerClassSpec? LoggerClassSpec, ImmutableEquatableArray Diagnostics, bool HasStringCreate)> items, SourceProductionContext context) + private static void EmitSource(ImmutableEquatableArray<(LoggerClassSpec LoggerClassSpec, bool HasStringCreate)> items, SourceProductionContext context) { - if (items.IsDefaultOrEmpty) + if (items.Count == 0) { return; } bool hasStringCreate = false; - var allLogClasses = new Dictionary(); // Use dictionary to deduplicate by class key - var reportedDiagnostics = new HashSet(); // Track reported diagnostics to avoid duplicates + var allLogClasses = new Dictionary(); // Deduplicate by class key foreach (var item in items) { - // Report diagnostics (note: pragma suppression doesn't work with trimmed locations - known Roslyn limitation) - // Use HashSet to deduplicate - each attributed method triggers parsing of entire class, producing duplicate diagnostics - if (item.Diagnostics is not null) + hasStringCreate |= item.HasStringCreate; + + // Build unique key including parent class chain to handle nested classes + string classKey = BuildClassKey(item.LoggerClassSpec); + + // Each attributed method in a partial class file produces the same LoggerClassSpec with all methods in that file. + // However, different partial class files produce different LoggerClassSpecs with different methods. Merge them. + if (!allLogClasses.TryGetValue(classKey, out LoggerClass? existingClass)) { - foreach (var diagnostic in item.Diagnostics) - { - if (reportedDiagnostics.Add(diagnostic)) - { - context.ReportDiagnostic(diagnostic.CreateDiagnostic()); - } - } + allLogClasses[classKey] = FromSpec(item.LoggerClassSpec); } - - if (item.LoggerClassSpec != null) + else { - hasStringCreate |= item.HasStringCreate; - - // Build unique key including parent class chain to handle nested classes - string classKey = BuildClassKey(item.LoggerClassSpec); + var newClass = FromSpec(item.LoggerClassSpec); - // Each attributed method in a partial class file produces the same LoggerClassSpec with all methods in that file. - // However, different partial class files (e.g., LevelTestExtensions.cs and LevelTestExtensions.WithDiagnostics.cs) - // produce different LoggerClassSpecs with different methods. Merge them. - if (!allLogClasses.TryGetValue(classKey, out LoggerClass? existingClass)) + var existingMethodKeys = new HashSet<(string Name, int EventId)>(); + foreach (var method in existingClass.Methods) { - allLogClasses[classKey] = FromSpec(item.LoggerClassSpec); + existingMethodKeys.Add((method.Name, method.EventId)); } - else - { - // Merge methods from different partial class files - var newClass = FromSpec(item.LoggerClassSpec); - - // Use HashSet for O(1) lookup to avoid O(N×M) complexity - var existingMethodKeys = new HashSet<(string Name, int EventId)>(); - foreach (var method in existingClass.Methods) - { - existingMethodKeys.Add((method.Name, method.EventId)); - } - foreach (var method in newClass.Methods) + foreach (var method in newClass.Methods) + { + if (existingMethodKeys.Add((method.Name, method.EventId))) { - // Only add methods that don't already exist (avoid duplicates from same file) - if (existingMethodKeys.Add((method.Name, method.EventId))) - { - existingClass.Methods.Add(method); - } + existingClass.Methods.Add(method); } } } diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Microsoft.Extensions.Logging.Generators.targets b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Microsoft.Extensions.Logging.Generators.targets index f1a42f8831ecfa..096dc2f89a3709 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Microsoft.Extensions.Logging.Generators.targets +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/Microsoft.Extensions.Logging.Generators.targets @@ -25,7 +25,6 @@ - diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs index a6cc6db209297d..a6bef486f6de2f 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs @@ -10,6 +10,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; using SourceGenerators.Tests; using Xunit; @@ -1426,5 +1427,35 @@ private static async Task> RunGenerator( return d; } + + [Fact] + public async Task Diagnostic_HasPragmaSuppressibleLocation() + { + // SYSLIB1017: MissingLogLevel (Error, but not NotConfigurable). + string code = """ + #pragma warning disable SYSLIB1017 + using Microsoft.Extensions.Logging; + + namespace Test + { + partial class C + { + [LoggerMessage(EventId = 0, Message = "M1")] + static partial void M1(ILogger logger); + } + } + """; + + Assembly[] refs = new[] { typeof(ILogger).Assembly, typeof(LoggerMessageAttribute).Assembly }; + using var workspace = RoslynTestUtils.CreateTestWorkspace(); + Project proj = RoslynTestUtils.CreateTestProject(workspace, refs) + .WithDocuments(new[] { code }); + Assert.True(proj.Solution.Workspace.TryApplyChanges(proj.Solution)); + Compilation comp = (await proj.GetCompilationAsync().ConfigureAwait(false))!; + var (diags, _) = RoslynTestUtils.RunGenerator(comp, new LoggerMessageGenerator()); + var effective = CompilationWithAnalyzers.GetEffectiveDiagnostics(diags, comp); + Diagnostic diagnostic = Assert.Single(effective, d => d.Id == "SYSLIB1017"); + Assert.True(diagnostic.IsSuppressed); + } } } diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Common/LoggerMessageTest.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Common/LoggerMessageTest.cs index a177db3ad35d7e..162d95b59d7d2e 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Common/LoggerMessageTest.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Common/LoggerMessageTest.cs @@ -455,7 +455,7 @@ private void AssertLogValues( if (expected == null || actual == null) { - throw EqualException.ForMismatchedValues(expected, actual); + throw EqualException.ForMismatchedValues(expected?.ToString(), actual?.ToString()); } if (ReferenceEquals(expected, actual)) diff --git a/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj b/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj index f89dbf1fab2d31..1a4cb17a51ccb4 100644 --- a/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj +++ b/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj @@ -105,8 +105,6 @@ Link="Common\Interop\Windows\Kernel32\Interop.GetProcessTimes.cs" /> - - - + diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Windows.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Windows.cs index cebd7469d43667..8a7e2b914c4850 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Windows.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Windows.cs @@ -428,10 +428,10 @@ private void SetWorkingSetLimitsCore(IntPtr? newMin, IntPtr? newMax, out IntPtr private unsafe bool StartWithCreateProcess(ProcessStartInfo startInfo) { // See knowledge base article Q190351 for an explanation of the following code. Noteworthy tricky points: - // * The handles are duplicated as non-inheritable before they are passed to CreateProcess so - // that the child process can not close them + // * The handles are duplicated as inheritable before they are passed to CreateProcess so + // that the child process can use them // * CreateProcess allows you to redirect all or none of the standard IO handles, so we use - // GetStdHandle for the handles that are not being redirected + // Console.OpenStandard*Handle for the handles that are not being redirected var commandLine = new ValueStringBuilder(stackalloc char[256]); BuildCommandLine(startInfo, ref commandLine); @@ -468,7 +468,7 @@ private unsafe bool StartWithCreateProcess(ProcessStartInfo startInfo) } else { - childInputPipeHandle = new SafeFileHandle(Interop.Kernel32.GetStdHandle(Interop.Kernel32.HandleTypes.STD_INPUT_HANDLE), false); + childInputPipeHandle = Console.OpenStandardInputHandle(); } if (startInfo.RedirectStandardOutput) @@ -477,7 +477,7 @@ private unsafe bool StartWithCreateProcess(ProcessStartInfo startInfo) } else { - childOutputPipeHandle = new SafeFileHandle(Interop.Kernel32.GetStdHandle(Interop.Kernel32.HandleTypes.STD_OUTPUT_HANDLE), false); + childOutputPipeHandle = Console.OpenStandardOutputHandle(); } if (startInfo.RedirectStandardError) @@ -486,7 +486,7 @@ private unsafe bool StartWithCreateProcess(ProcessStartInfo startInfo) } else { - childErrorPipeHandle = new SafeFileHandle(Interop.Kernel32.GetStdHandle(Interop.Kernel32.HandleTypes.STD_ERROR_HANDLE), false); + childErrorPipeHandle = Console.OpenStandardErrorHandle(); } startupInfo.hStdInput = childInputPipeHandle.DangerousGetHandle(); @@ -800,15 +800,6 @@ private SafeProcessHandle GetProcessHandle(int access, bool throwIfExited = true } } - private static void CreatePipeWithSecurityAttributes(out SafeFileHandle hReadPipe, out SafeFileHandle hWritePipe, ref Interop.Kernel32.SECURITY_ATTRIBUTES lpPipeAttributes, int nSize) - { - bool ret = Interop.Kernel32.CreatePipe(out hReadPipe, out hWritePipe, ref lpPipeAttributes, nSize); - if (!ret || hReadPipe.IsInvalid || hWritePipe.IsInvalid) - { - throw new Win32Exception(); - } - } - // Using synchronous Anonymous pipes for process input/output redirection means we would end up // wasting a worker threadpool thread per pipe instance. Overlapped pipe IO is desirable, since // it will take advantage of the NT IO completion port infrastructure. But we can't really use @@ -818,47 +809,31 @@ private static void CreatePipeWithSecurityAttributes(out SafeFileHandle hReadPip // for synchronous I/O and hence they can work fine with ReadFile/WriteFile synchronously! private static void CreatePipe(out SafeFileHandle parentHandle, out SafeFileHandle childHandle, bool parentInputs) { - Interop.Kernel32.SECURITY_ATTRIBUTES securityAttributesParent = default; - securityAttributesParent.bInheritHandle = Interop.BOOL.TRUE; + SafeFileHandle.CreateAnonymousPipe(out SafeFileHandle readHandle, out SafeFileHandle writeHandle); - SafeFileHandle? hTmp = null; - try - { - if (parentInputs) - { - CreatePipeWithSecurityAttributes(out childHandle, out hTmp, ref securityAttributesParent, 0); - } - else - { - CreatePipeWithSecurityAttributes(out hTmp, - out childHandle, - ref securityAttributesParent, - 0); - } - // Duplicate the parent handle to be non-inheritable so that the child process - // doesn't have access. This is done for correctness sake, exact reason is unclear. - // One potential theory is that child process can do something brain dead like - // closing the parent end of the pipe and there by getting into a blocking situation - // as parent will not be draining the pipe at the other end anymore. - IntPtr currentProcHandle = Interop.Kernel32.GetCurrentProcess(); - if (!Interop.Kernel32.DuplicateHandle(currentProcHandle, - hTmp, - currentProcHandle, - out parentHandle, - 0, - false, - Interop.Kernel32.HandleOptions.DUPLICATE_SAME_ACCESS)) - { - throw new Win32Exception(); - } - } - finally + // parentInputs=true: parent writes to pipe, child reads (stdin redirect). + // parentInputs=false: parent reads from pipe, child writes (stdout/stderr redirect). + parentHandle = parentInputs ? writeHandle : readHandle; + SafeFileHandle hTmpChild = parentInputs ? readHandle : writeHandle; + + // Duplicate the child handle to be inheritable so that the child process + // has access. The original non-inheritable handle is closed afterwards. + IntPtr currentProcHandle = Interop.Kernel32.GetCurrentProcess(); + if (!Interop.Kernel32.DuplicateHandle(currentProcHandle, + hTmpChild, + currentProcHandle, + out childHandle, + 0, + bInheritHandle: true, + Interop.Kernel32.HandleOptions.DUPLICATE_SAME_ACCESS)) { - if (hTmp != null && !hTmp.IsInvalid) - { - hTmp.Dispose(); - } + int lastError = Marshal.GetLastWin32Error(); + parentHandle.Dispose(); + hTmpChild.Dispose(); + throw new Win32Exception(lastError); } + + hTmpChild.Dispose(); } private static string GetEnvironmentVariablesBlock(DictionaryWrapper sd) diff --git a/src/libraries/System.IO.Pipes/tests/PipeStreamConformanceTests.cs b/src/libraries/System.IO.Pipes/tests/PipeStreamConformanceTests.cs index 867e12206528de..dfe4408e39b800 100644 --- a/src/libraries/System.IO.Pipes/tests/PipeStreamConformanceTests.cs +++ b/src/libraries/System.IO.Pipes/tests/PipeStreamConformanceTests.cs @@ -7,6 +7,7 @@ using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; +using Microsoft.Win32.SafeHandles; using Xunit; namespace System.IO.Pipes.Tests @@ -782,6 +783,29 @@ protected override (AnonymousPipeServerStream Server, AnonymousPipeClientStream } } + public class AnonymousPipeTest_SafeFileHandle_CreateAnonymousPipe : AnonymousPipeStreamConformanceTests + { + protected override (AnonymousPipeServerStream Server, AnonymousPipeClientStream Client) CreateServerAndClientStreams() + { + SafeFileHandle.CreateAnonymousPipe(out SafeFileHandle readHandle, out SafeFileHandle writeHandle); + + SafePipeHandle readPipeHandle = TransferOwnershipToPipeHandle(readHandle); + SafePipeHandle writePipeHandle = TransferOwnershipToPipeHandle(writeHandle); + + AnonymousPipeServerStream server = new(PipeDirection.Out, serverSafePipeHandle: writePipeHandle, clientSafePipeHandle: readPipeHandle); + AnonymousPipeClientStream client = new(PipeDirection.In, server.ClientSafePipeHandle); + return (server, client); + } + + private static SafePipeHandle TransferOwnershipToPipeHandle(SafeFileHandle handle) + { + SafePipeHandle pipeHandle = new(handle.DangerousGetHandle(), ownsHandle: true); + handle.SetHandleAsInvalid(); + handle.Dispose(); + return pipeHandle; + } + } + public abstract class NamedPipeTest_ServerOut_ClientIn : NamedPipeStreamConformanceTests { protected override NamedPipeServerStream CreateServerStream(string pipeName, int maxInstances = 1) => diff --git a/src/libraries/System.Linq.Parallel/src/System.Linq.Parallel.csproj b/src/libraries/System.Linq.Parallel/src/System.Linq.Parallel.csproj index fdf895c25448ff..1dd9a0b6af2973 100644 --- a/src/libraries/System.Linq.Parallel/src/System.Linq.Parallel.csproj +++ b/src/libraries/System.Linq.Parallel/src/System.Linq.Parallel.csproj @@ -9,10 +9,7 @@ $([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) - true - $(DefineConstants);FEATURE_WASM_MANAGED_THREADS - true - $(DefineConstants);FEATURE_SINGLE_THREADED + $(DefineConstants);FEATURE_WASM_MANAGED_THREADS diff --git a/src/libraries/System.Net.Http/src/System.Net.Http.csproj b/src/libraries/System.Net.Http/src/System.Net.Http.csproj index 782405cfa47233..6a73f9c1ab1701 100644 --- a/src/libraries/System.Net.Http/src/System.Net.Http.csproj +++ b/src/libraries/System.Net.Http/src/System.Net.Http.csproj @@ -11,7 +11,6 @@ $([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) SR.PlatformNotSupported_NetHttp - true $(DefineConstants);SYSNETHTTP_NO_OPENSSL $(DefineConstants);TARGET_MOBILE $(DefineConstants);TARGET_ANDROID @@ -20,7 +19,6 @@ $(DefineConstants);TARGET_TVOS $(DefineConstants);TARGET_BROWSER $(DefineConstants);TARGET_WASI - $(DefineConstants);FEATURE_WASM_MANAGED_THREADS true diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIPGlobalProperties.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIPGlobalProperties.cs index 3090e47ea138b4..b534afb1272851 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIPGlobalProperties.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIPGlobalProperties.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics; using System.Net.Sockets; using System.Runtime.InteropServices; using System.Threading.Tasks; @@ -73,46 +74,29 @@ public override bool IsWinsProxy public override TcpConnectionInformation[] GetActiveTcpConnections() { List list = new List(); - List connections = GetAllTcpConnections(); - foreach (TcpConnectionInformation connection in connections) - { - if (connection.State != TcpState.Listen) - { - list.Add(connection); - } - } - + GetAllTcpConnections(list, null); return list.ToArray(); } public override IPEndPoint[] GetActiveTcpListeners() { List list = new List(); - List connections = GetAllTcpConnections(); - foreach (TcpConnectionInformation connection in connections) - { - if (connection.State == TcpState.Listen) - { - list.Add(connection.LocalEndPoint); - } - } - + GetAllTcpConnections(null, list); return list.ToArray(); } - /// - /// Gets the active TCP connections. Uses the native GetTcpTable API. - private static unsafe List GetAllTcpConnections() + /// Gets the active TCP connections. Uses the native GetExtendedTcpTable API. + private static unsafe void GetAllTcpConnections(List? connections, List? listening) { uint size = 0; uint result; - List tcpConnections = new List(); // Check if it supports IPv4 for IPv6 only modes. if (Socket.OSSupportsIPv4) { // Get the buffer size needed. - result = Interop.IpHlpApi.GetTcpTable(IntPtr.Zero, &size, order: true); + result = Interop.IpHlpApi.GetExtendedTcpTable(0, &size, order: true, (uint)AddressFamily.InterNetwork, + connections is null ? Interop.IpHlpApi.TcpTableClass.TcpTableBasicListener : Interop.IpHlpApi.TcpTableClass.TcpTableBasicAll, 0); while (result == Interop.IpHlpApi.ERROR_INSUFFICIENT_BUFFER) { @@ -120,24 +104,26 @@ private static unsafe List GetAllTcpConnections( IntPtr buffer = Marshal.AllocHGlobal((int)size); try { - result = Interop.IpHlpApi.GetTcpTable(buffer, &size, order: true); + result = Interop.IpHlpApi.GetExtendedTcpTable(buffer, &size, order: true, (uint)AddressFamily.InterNetwork, + connections is null ? Interop.IpHlpApi.TcpTableClass.TcpTableBasicListener : Interop.IpHlpApi.TcpTableClass.TcpTableBasicAll, 0); if (result == Interop.IpHlpApi.ERROR_SUCCESS) { - var span = new ReadOnlySpan((byte*)buffer, (int)size); - - // The table info just gives us the number of rows. - ref readonly Interop.IpHlpApi.MibTcpTable tcpTableInfo = ref MemoryMarshal.AsRef(span); - - if (tcpTableInfo.numberOfEntries > 0) + var table = (Interop.IpHlpApi.MibTcpTable*)buffer; + if (table->NumEntries > 0) { - // Skip over the tableinfo to get the inline rows. - span = span.Slice(sizeof(Interop.IpHlpApi.MibTcpTable)); - - for (int i = 0; i < tcpTableInfo.numberOfEntries; i++) + var span = new ReadOnlySpan(&table->FirstEntry, (int)table->NumEntries); + Debug.Assert(sizeof(uint) + sizeof(Interop.IpHlpApi.MibTcpRow) * span.Length <= size); + foreach (ref readonly Interop.IpHlpApi.MibTcpRow entry in span) { - tcpConnections.Add(new SystemTcpConnectionInformation(in MemoryMarshal.AsRef(span))); - span = span.Slice(sizeof(Interop.IpHlpApi.MibTcpRow)); + if (entry.State == TcpState.Listen) + { + listening?.Add(entry.LocalEndPoint); + } + else + { + connections?.Add(new SystemTcpConnectionInformation(in entry)); + } } } } @@ -159,9 +145,8 @@ private static unsafe List GetAllTcpConnections( { // Get the buffer size needed. size = 0; - result = Interop.IpHlpApi.GetExtendedTcpTable(IntPtr.Zero, &size, order: true, - (uint)AddressFamily.InterNetworkV6, - Interop.IpHlpApi.TcpTableClass.TcpTableOwnerPidAll, 0); + result = Interop.IpHlpApi.GetExtendedTcpTable(0, &size, order: true, (uint)AddressFamily.InterNetworkV6, + connections is null ? Interop.IpHlpApi.TcpTableClass.TcpTableOwnerPidListener : Interop.IpHlpApi.TcpTableClass.TcpTableOwnerPidAll, 0); while (result == Interop.IpHlpApi.ERROR_INSUFFICIENT_BUFFER) { @@ -169,27 +154,26 @@ private static unsafe List GetAllTcpConnections( IntPtr buffer = Marshal.AllocHGlobal((int)size); try { - result = Interop.IpHlpApi.GetExtendedTcpTable(buffer, &size, order: true, - (uint)AddressFamily.InterNetworkV6, - Interop.IpHlpApi.TcpTableClass.TcpTableOwnerPidAll, 0); + result = Interop.IpHlpApi.GetExtendedTcpTable(buffer, &size, order: true, (uint)AddressFamily.InterNetworkV6, + connections is null ? Interop.IpHlpApi.TcpTableClass.TcpTableOwnerPidListener : Interop.IpHlpApi.TcpTableClass.TcpTableOwnerPidAll, 0); + if (result == Interop.IpHlpApi.ERROR_SUCCESS) { - var span = new ReadOnlySpan((byte*)buffer, (int)size); - - // The table info just gives us the number of rows. - ref readonly Interop.IpHlpApi.MibTcp6TableOwnerPid tcpTable6OwnerPid = ref MemoryMarshal.AsRef(span); - - if (tcpTable6OwnerPid.numberOfEntries > 0) + var table = (Interop.IpHlpApi.MibTcp6TableOwnerPid*)buffer; + if (table->NumEntries > 0) { - // Skip over the tableinfo to get the inline rows. - span = span.Slice(sizeof(Interop.IpHlpApi.MibTcp6TableOwnerPid)); - - for (int i = 0; i < tcpTable6OwnerPid.numberOfEntries; i++) + var span = new ReadOnlySpan(&table->FirstEntry, (int)table->NumEntries); + Debug.Assert(sizeof(uint) + sizeof(Interop.IpHlpApi.MibTcp6RowOwnerPid) * span.Length <= size); + foreach (ref readonly Interop.IpHlpApi.MibTcp6RowOwnerPid entry in span) { - tcpConnections.Add(new SystemTcpConnectionInformation(in MemoryMarshal.AsRef(span))); - - // We increment the pointer to the next row. - span = span.Slice(sizeof(Interop.IpHlpApi.MibTcp6RowOwnerPid)); + if (entry.State == TcpState.Listen) + { + listening?.Add(entry.LocalEndPoint); + } + else + { + connections?.Add(new SystemTcpConnectionInformation(in entry)); + } } } } @@ -206,11 +190,9 @@ private static unsafe List GetAllTcpConnections( throw new NetworkInformationException((int)result); } } - - return tcpConnections; } - /// Gets the active UDP listeners. Uses the native GetUdpTable API. + /// Gets the active UDP listeners. Uses the native GetExtendedUdpTable API. public override unsafe IPEndPoint[] GetActiveUdpListeners() { uint size = 0; @@ -221,37 +203,28 @@ public override unsafe IPEndPoint[] GetActiveUdpListeners() if (Socket.OSSupportsIPv4) { // Get the buffer size needed. - result = Interop.IpHlpApi.GetUdpTable(IntPtr.Zero, &size, order: true); + result = Interop.IpHlpApi.GetExtendedUdpTable(0, &size, order: true, (uint)AddressFamily.InterNetwork, + Interop.IpHlpApi.UdpTableClass.UdpTableBasic, 0); + while (result == Interop.IpHlpApi.ERROR_INSUFFICIENT_BUFFER) { // Allocate the buffer and get the UDP table. IntPtr buffer = Marshal.AllocHGlobal((int)size); - try { - result = Interop.IpHlpApi.GetUdpTable(buffer, &size, order: true); + result = Interop.IpHlpApi.GetExtendedUdpTable(buffer, &size, order: true, (uint)AddressFamily.InterNetwork, + Interop.IpHlpApi.UdpTableClass.UdpTableBasic, 0); if (result == Interop.IpHlpApi.ERROR_SUCCESS) { - var span = new ReadOnlySpan((byte*)buffer, (int)size); - - // The table info just gives us the number of rows. - ref readonly Interop.IpHlpApi.MibUdpTable udpTableInfo = ref MemoryMarshal.AsRef(span); - - if (udpTableInfo.numberOfEntries > 0) + var table = (Interop.IpHlpApi.MibUdpTable*)buffer; + if (table->NumEntries > 0) { - // Skip over the tableinfo to get the inline rows. - span = span.Slice(sizeof(Interop.IpHlpApi.MibUdpTable)); - - for (int i = 0; i < udpTableInfo.numberOfEntries; i++) + var span = new ReadOnlySpan(&table->FirstEntry, (int)table->NumEntries); + Debug.Assert(sizeof(uint) + sizeof(Interop.IpHlpApi.MibUdpRow) * span.Length <= size); + foreach (ref readonly Interop.IpHlpApi.MibUdpRow entry in span) { - ref readonly Interop.IpHlpApi.MibUdpRow udpRow = ref MemoryMarshal.AsRef(span); - int localPort = udpRow.localPort1 << 8 | udpRow.localPort2; - - udpListeners.Add(new IPEndPoint(udpRow.localAddr, (int)localPort)); - - // We increment the pointer to the next row. - span = span.Slice(sizeof(Interop.IpHlpApi.MibUdpRow)); + udpListeners.Add(entry.LocalEndPoint); } } } @@ -288,26 +261,14 @@ public override unsafe IPEndPoint[] GetActiveUdpListeners() if (result == Interop.IpHlpApi.ERROR_SUCCESS) { - var span = new ReadOnlySpan((byte*)buffer, (int)size); - - // The table info just gives us the number of rows. - ref readonly Interop.IpHlpApi.MibUdp6TableOwnerPid udp6TableOwnerPid = ref MemoryMarshal.AsRef(span); - - if (udp6TableOwnerPid.numberOfEntries > 0) + var table = (Interop.IpHlpApi.MibUdp6TableOwnerPid*)buffer; + if (table->NumEntries > 0) { - // Skip over the tableinfo to get the inline rows. - span = span.Slice(sizeof(Interop.IpHlpApi.MibUdp6TableOwnerPid)); - - for (int i = 0; i < udp6TableOwnerPid.numberOfEntries; i++) + var span = new ReadOnlySpan(&table->FirstEntry, (int)table->NumEntries); + Debug.Assert(sizeof(uint) + sizeof(Interop.IpHlpApi.MibUdp6RowOwnerPid) * span.Length <= size); + foreach (ref readonly Interop.IpHlpApi.MibUdp6RowOwnerPid entry in span) { - ref readonly Interop.IpHlpApi.MibUdp6RowOwnerPid udp6RowOwnerPid = ref MemoryMarshal.AsRef(span); - int localPort = udp6RowOwnerPid.localPort1 << 8 | udp6RowOwnerPid.localPort2; - - udpListeners.Add(new IPEndPoint(new IPAddress(udp6RowOwnerPid.localAddrAsSpan, - udp6RowOwnerPid.localScopeId), localPort)); - - // We increment the pointer to the next row. - span = span.Slice(sizeof(Interop.IpHlpApi.MibUdp6RowOwnerPid)); + udpListeners.Add(entry.LocalEndPoint); } } } diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemTcpConnection.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemTcpConnection.cs index 0237fcb3cb1c79..b5db54313fd5d8 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemTcpConnection.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemTcpConnection.cs @@ -12,29 +12,17 @@ internal sealed class SystemTcpConnectionInformation : TcpConnectionInformation internal SystemTcpConnectionInformation(in Interop.IpHlpApi.MibTcpRow row) { - _state = row.state; - - // Port is returned in Big-Endian - most significant bit on left. - // Unfortunately, its done at the word level and not the DWORD level. - int localPort = row.localPort1 << 8 | row.localPort2; - int remotePort = ((_state == TcpState.Listen) ? 0 : row.remotePort1 << 8 | row.remotePort2); - - _localEndPoint = new IPEndPoint(row.localAddr, (int)localPort); - _remoteEndPoint = new IPEndPoint(row.remoteAddr, (int)remotePort); + _state = row.State; + _localEndPoint = row.LocalEndPoint; + _remoteEndPoint = row.RemoteEndPoint; } // IPV6 version of the Tcp row. internal SystemTcpConnectionInformation(in Interop.IpHlpApi.MibTcp6RowOwnerPid row) { - _state = row.state; - - // Port is returned in Big-Endian - most significant bit on left. - // Unfortunately, its done at the word level and not the DWORD level. - int localPort = row.localPort1 << 8 | row.localPort2; - int remotePort = ((_state == TcpState.Listen) ? 0 : row.remotePort1 << 8 | row.remotePort2); - - _localEndPoint = new IPEndPoint(new IPAddress(row.localAddrAsSpan, row.localScopeId), (int)localPort); - _remoteEndPoint = new IPEndPoint(new IPAddress(row.remoteAddrAsSpan, row.remoteScopeId), (int)remotePort); + _state = row.State; + _localEndPoint = row.LocalEndPoint; + _remoteEndPoint = row.RemoteEndPoint; } public override TcpState State { get { return _state; } } diff --git a/src/libraries/System.Net.Security/src/System.Net.Security.csproj b/src/libraries/System.Net.Security/src/System.Net.Security.csproj index fa9f8b24212436..61d4b05c9aae6b 100644 --- a/src/libraries/System.Net.Security/src/System.Net.Security.csproj +++ b/src/libraries/System.Net.Security/src/System.Net.Security.csproj @@ -23,8 +23,6 @@ ReferenceAssemblyExclusions.txt - - diff --git a/src/libraries/System.Net.Security/src/System/Net/NegotiateAuthenticationPal.ManagedNtlm.cs b/src/libraries/System.Net.Security/src/System/Net/NegotiateAuthenticationPal.ManagedNtlm.cs index 497d95f06e24c6..d1fa9a8490bf89 100644 --- a/src/libraries/System.Net.Security/src/System/Net/NegotiateAuthenticationPal.ManagedNtlm.cs +++ b/src/libraries/System.Net.Security/src/System/Net/NegotiateAuthenticationPal.ManagedNtlm.cs @@ -124,15 +124,25 @@ private enum AvId } [StructLayout(LayoutKind.Sequential)] - private unsafe struct MessageField + private struct MessageField { - public ushort Length; - public ushort MaximumLength; + private ushort _length; + private ushort _maximumLength; private int _payloadOffset; + public ushort Length + { + readonly get => BitConverter.IsLittleEndian ? _length : BinaryPrimitives.ReverseEndianness(_length); + set => _length = BitConverter.IsLittleEndian ? value : BinaryPrimitives.ReverseEndianness(value); + } + public ushort MaximumLength + { + readonly get => BitConverter.IsLittleEndian ? _maximumLength : BinaryPrimitives.ReverseEndianness(_maximumLength); + set => _maximumLength = BitConverter.IsLittleEndian ? value : BinaryPrimitives.ReverseEndianness(value); + } public int PayloadOffset { - readonly get =>BitConverter.IsLittleEndian? _payloadOffset: BinaryPrimitives.ReverseEndianness(_payloadOffset); - set =>_payloadOffset = BitConverter.IsLittleEndian? value: BinaryPrimitives.ReverseEndianness(value); + readonly get => BitConverter.IsLittleEndian ? _payloadOffset : BinaryPrimitives.ReverseEndianness(_payloadOffset); + set => _payloadOffset = BitConverter.IsLittleEndian ? value : BinaryPrimitives.ReverseEndianness(value); } } @@ -158,8 +168,8 @@ private unsafe struct Version public byte CurrentRevision; public ushort ProductBuild { - readonly get =>BitConverter.IsLittleEndian? _productBuild: BinaryPrimitives.ReverseEndianness(_productBuild); - set =>_productBuild = BitConverter.IsLittleEndian? value: BinaryPrimitives.ReverseEndianness(value); + readonly get => BitConverter.IsLittleEndian ? _productBuild : BinaryPrimitives.ReverseEndianness(_productBuild); + set => _productBuild = BitConverter.IsLittleEndian ? value : BinaryPrimitives.ReverseEndianness(value); } } @@ -174,8 +184,8 @@ private unsafe struct NegotiateMessage public Version Version; public Flags Flags { - readonly get =>BitConverter.IsLittleEndian? _flags: (Flags)BinaryPrimitives.ReverseEndianness((uint)_flags); - set =>_flags = BitConverter.IsLittleEndian? value: (Flags)BinaryPrimitives.ReverseEndianness((uint)value); + readonly get => BitConverter.IsLittleEndian ? _flags : (Flags)BinaryPrimitives.ReverseEndianness((uint)_flags); + set => _flags = BitConverter.IsLittleEndian ? value : (Flags)BinaryPrimitives.ReverseEndianness((uint)value); } } @@ -185,11 +195,16 @@ private unsafe struct ChallengeMessage { public MessageHeader Header; public MessageField TargetName; - public Flags Flags; + private Flags _flags; public fixed byte ServerChallenge[ChallengeLength]; private ulong _unused; public MessageField TargetInfo; public Version Version; + public Flags Flags + { + readonly get => BitConverter.IsLittleEndian ? _flags : (Flags)BinaryPrimitives.ReverseEndianness((uint)_flags); + set => _flags = BitConverter.IsLittleEndian ? value : (Flags)BinaryPrimitives.ReverseEndianness((uint)value); + } } // TYPE 3 message @@ -203,9 +218,14 @@ private unsafe struct AuthenticateMessage public MessageField UserName; public MessageField Workstation; public MessageField EncryptedRandomSessionKey; - public Flags Flags; + private Flags _flags; public Version Version; public fixed byte Mic[16]; + public Flags Flags + { + readonly get => BitConverter.IsLittleEndian ? _flags : (Flags)BinaryPrimitives.ReverseEndianness((uint)_flags); + set => _flags = BitConverter.IsLittleEndian ? value : (Flags)BinaryPrimitives.ReverseEndianness((uint)value); + } } // Set temp to ConcatenationOf(Responserversion, HiResponserversion, Z(6), Time, ClientChallenge, Z(4), ServerName, Z(4)) @@ -218,10 +238,15 @@ private unsafe struct NtChallengeResponse private byte _reserved1; private byte _reserved2; private int _reserved3; - public long Time; + private long _time; public fixed byte ClientChallenge[ChallengeLength]; private int _reserved4; public fixed byte ServerInfo[4]; // Has to be non-zero size, so set it to the Z(4) padding + public long Time + { + readonly get => BitConverter.IsLittleEndian ? _time : BinaryPrimitives.ReverseEndianness(_time); + set => _time = BitConverter.IsLittleEndian ? value : BinaryPrimitives.ReverseEndianness(value); + } } public override bool IsAuthenticated => _isAuthenticated; @@ -321,42 +346,29 @@ private static unsafe void CreateNtlmNegotiateMessage(Span asBytes, Flags message.Version = s_version; } - private static unsafe int GetFieldLength(MessageField field) - { - ReadOnlySpan span = new ReadOnlySpan(&field, sizeof(MessageField)); - return BinaryPrimitives.ReadInt16LittleEndian(span); - } - - private static unsafe int GetFieldOffset(MessageField field) - { - ReadOnlySpan span = new ReadOnlySpan(&field, sizeof(MessageField)); - return BinaryPrimitives.ReadInt16LittleEndian(span.Slice(4)); - } - private static ReadOnlySpan GetField(MessageField field, ReadOnlySpan payload) { - int offset = GetFieldOffset(field); - int length = GetFieldLength(field); + int offset = field.PayloadOffset; + int length = field.Length; if (length == 0 || offset + length > payload.Length) { return ReadOnlySpan.Empty; } - return payload.Slice(GetFieldOffset(field), GetFieldLength(field)); + return payload.Slice(offset, length); } - private static unsafe void SetField(ref MessageField field, int length, int offset) + private static void SetField(ref MessageField field, int length, int offset) { if (length is < 0 or > short.MaxValue) { throw new Win32Exception(NTE_FAIL); } - Span span = MemoryMarshal.AsBytes(new Span(ref field)); - BinaryPrimitives.WriteInt16LittleEndian(span, (short)length); - BinaryPrimitives.WriteInt16LittleEndian(span.Slice(2), (short)length); - BinaryPrimitives.WriteInt32LittleEndian(span.Slice(4), offset); + field.Length = (ushort)length; + field.MaximumLength = (ushort)length; + field.PayloadOffset = offset; } private static void AddToPayload(ref MessageField field, ReadOnlySpan data, Span payload, ref int offset) @@ -589,7 +601,7 @@ private static byte[] DeriveKey(ReadOnlySpan exportedSessionKey, ReadOnlyS return null; } - Flags flags = BitConverter.IsLittleEndian ? challengeMessage.Flags : (Flags)BinaryPrimitives.ReverseEndianness((uint)challengeMessage.Flags); + Flags flags = challengeMessage.Flags; ReadOnlySpan targetName = GetField(challengeMessage.TargetName, blob); // Only NTLMv2 with MIC is supported diff --git a/src/libraries/System.Net.Security/src/System/Net/NegotiateAuthenticationPal.ManagedSpnego.cs b/src/libraries/System.Net.Security/src/System/Net/NegotiateAuthenticationPal.ManagedSpnego.cs index 1ddb004769037f..ad860ec7595f5b 100644 --- a/src/libraries/System.Net.Security/src/System/Net/NegotiateAuthenticationPal.ManagedSpnego.cs +++ b/src/libraries/System.Net.Security/src/System/Net/NegotiateAuthenticationPal.ManagedSpnego.cs @@ -222,8 +222,8 @@ private IEnumerable> EnumerateMechanisms() try { - AsnValueReader reader = new AsnValueReader(challenge, AsnEncodingRules.DER); - AsnValueReader challengeReader = reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, (int)NegotiationToken.NegTokenResp)); + ValueAsnReader reader = new ValueAsnReader(challenge, AsnEncodingRules.DER); + ValueAsnReader challengeReader = reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, (int)NegotiationToken.NegTokenResp)); reader.ThrowIfNotEmpty(); // NegTokenResp ::= SEQUENCE { @@ -245,28 +245,28 @@ private IEnumerable> EnumerateMechanisms() if (challengeReader.HasData && challengeReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, (int)NegTokenResp.NegState))) { - AsnValueReader valueReader = challengeReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, (int)NegTokenResp.NegState)); + ValueAsnReader valueReader = challengeReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, (int)NegTokenResp.NegState)); state = valueReader.ReadEnumeratedValue(); valueReader.ThrowIfNotEmpty(); } if (challengeReader.HasData && challengeReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, (int)NegTokenResp.SupportedMech))) { - AsnValueReader valueReader = challengeReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, (int)NegTokenResp.SupportedMech)); + ValueAsnReader valueReader = challengeReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, (int)NegTokenResp.SupportedMech)); mech = valueReader.ReadObjectIdentifier(); valueReader.ThrowIfNotEmpty(); } if (challengeReader.HasData && challengeReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, (int)NegTokenResp.ResponseToken))) { - AsnValueReader valueReader = challengeReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, (int)NegTokenResp.ResponseToken)); + ValueAsnReader valueReader = challengeReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, (int)NegTokenResp.ResponseToken)); blob = valueReader.ReadOctetString(); valueReader.ThrowIfNotEmpty(); } if (challengeReader.HasData && challengeReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, (int)NegTokenResp.MechListMIC))) { - AsnValueReader valueReader = challengeReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, (int)NegTokenResp.MechListMIC)); + ValueAsnReader valueReader = challengeReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, (int)NegTokenResp.MechListMIC)); mechListMIC = valueReader.ReadOctetString(); valueReader.ThrowIfNotEmpty(); } diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SendPacketsElement.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SendPacketsElement.cs index ce6254df95fed6..038863b0a7f728 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SendPacketsElement.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SendPacketsElement.cs @@ -49,7 +49,9 @@ public SendPacketsElement(FileStream fileStream, long offset, int count, bool en { ArgumentNullException.ThrowIfNull(fileStream); - if (!fileStream.IsAsync) + // Async IO for regular files is only supported on Windows. On Unix, FileStream.IsAsync is always + // false for regular files, because Unix does not support O_NONBLOCK for regular files. + if (!fileStream.IsAsync && OperatingSystem.IsWindows()) { throw new ArgumentException(SR.net_sockets_sendpackelement_FileStreamMustBeAsync, nameof(fileStream)); } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendPacketsElementTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendPacketsElementTest.cs index 1c791e67c99ac2..59bb692db2c5bd 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendPacketsElementTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendPacketsElementTest.cs @@ -714,6 +714,7 @@ public void FileStreamCtorNegCount_ArgumentOutOfRangeException() } [Fact] + [PlatformSpecific(TestPlatforms.Windows)] // FileStream.IsAsync is always false on Unix for regular files [ActiveIssue("https://github.com/dotnet/runtime/issues/85690", TestPlatforms.Wasi)] public void FileStreamCtorSynchronous_ArgumentException() { diff --git a/src/libraries/System.Net.WebSockets.Client/src/System.Net.WebSockets.Client.csproj b/src/libraries/System.Net.WebSockets.Client/src/System.Net.WebSockets.Client.csproj index 1c208e4ecd655d..5b43963fcdb389 100644 --- a/src/libraries/System.Net.WebSockets.Client/src/System.Net.WebSockets.Client.csproj +++ b/src/libraries/System.Net.WebSockets.Client/src/System.Net.WebSockets.Client.csproj @@ -11,9 +11,8 @@ $([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) - true $(DefineConstants);TARGET_BROWSER - $(DefineConstants);FEATURE_WASM_MANAGED_THREADS + $(DefineConstants);FEATURE_WASM_MANAGED_THREADS true diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs index 2f886acc65240d..577656f536c156 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorShape.cs @@ -853,12 +853,22 @@ public static TensorShape Create(T[]? array) if (array is not null) { int linearLength = array.Length; + nint stride = 1; + + TensorFlags flags = TensorFlags.IsDense | TensorFlags.HasAnyDenseDimensions; + + if (linearLength <= 1) + { + stride = 0; + flags |= TensorFlags.IsBroadcast; + } + return new TensorShape( flattenedLength: linearLength, linearLength: linearLength, lengths: [linearLength], - strides: [1], - TensorFlags.IsDense | TensorFlags.HasAnyDenseDimensions + strides: [stride], + flags ); } return default; @@ -908,14 +918,22 @@ public static TensorShape Create(ref readonly T reference, nint linearLength, { if (!Unsafe.IsNullRef(in reference)) { + nint stride = 1; + TensorFlags flags = pinned ? TensorFlags.IsPinned : TensorFlags.None; flags |= TensorFlags.IsDense | TensorFlags.HasAnyDenseDimensions; + if (linearLength <= 1) + { + stride = 0; + flags |= TensorFlags.IsBroadcast; + } + return new TensorShape( flattenedLength: linearLength, linearLength: linearLength, lengths: [linearLength], - strides: [1], + strides: [stride], flags ); } diff --git a/src/libraries/System.Numerics.Tensors/tests/ReadOnlyTensorSpanTests.cs b/src/libraries/System.Numerics.Tensors/tests/ReadOnlyTensorSpanTests.cs index 3755acecd5f356..0490d75771c061 100644 --- a/src/libraries/System.Numerics.Tensors/tests/ReadOnlyTensorSpanTests.cs +++ b/src/libraries/System.Numerics.Tensors/tests/ReadOnlyTensorSpanTests.cs @@ -255,7 +255,7 @@ public static void ReadOnlyTensorSpanArrayConstructorTests() Assert.Equal(1, spanInt.Rank); Assert.Equal(0, spanInt.Lengths[0]); Assert.Equal(0, spanInt.FlattenedLength); - Assert.Equal(1, spanInt.Strides[0]); + Assert.Equal(0, spanInt.Strides[0]); // Make sure it still throws on index 0 Assert.Throws(() => { var spanInt = new ReadOnlyTensorSpan(b); diff --git a/src/libraries/System.Numerics.Tensors/tests/TensorPrimitives.Generic.cs b/src/libraries/System.Numerics.Tensors/tests/TensorPrimitives.Generic.cs index b802e29bb46435..775ec9b7e920e0 100644 --- a/src/libraries/System.Numerics.Tensors/tests/TensorPrimitives.Generic.cs +++ b/src/libraries/System.Numerics.Tensors/tests/TensorPrimitives.Generic.cs @@ -2723,7 +2723,7 @@ protected override void AssertEqualTolerance(T expected, T actual, T? tolerance { if (!Helpers.IsEqualWithTolerance(expected, actual, tolerance)) { - throw EqualException.ForMismatchedValues(expected, actual); + throw EqualException.ForMismatchedValues($"{expected}", $"{actual}"); } } diff --git a/src/libraries/System.Numerics.Tensors/tests/TensorPrimitives.NonGeneric.Single.cs b/src/libraries/System.Numerics.Tensors/tests/TensorPrimitives.NonGeneric.Single.cs index d6df8365c59db6..707e27d2fae419 100644 --- a/src/libraries/System.Numerics.Tensors/tests/TensorPrimitives.NonGeneric.Single.cs +++ b/src/libraries/System.Numerics.Tensors/tests/TensorPrimitives.NonGeneric.Single.cs @@ -108,7 +108,7 @@ protected override void AssertEqualTolerance(float expected, float actual, float { if (!Helpers.IsEqualWithTolerance(expected, actual, tolerance)) { - throw EqualException.ForMismatchedValues(expected, actual); + throw EqualException.ForMismatchedValues(expected.ToString(), actual.ToString()); } } diff --git a/src/libraries/System.Numerics.Tensors/tests/TensorSpanTests.cs b/src/libraries/System.Numerics.Tensors/tests/TensorSpanTests.cs index 77b295a8c36f9d..f1a1c736e94e16 100644 --- a/src/libraries/System.Numerics.Tensors/tests/TensorSpanTests.cs +++ b/src/libraries/System.Numerics.Tensors/tests/TensorSpanTests.cs @@ -711,7 +711,7 @@ public static void TensorSpanArrayConstructorTests() Assert.Equal(1, spanInt.Rank); Assert.Equal(0, spanInt.Lengths[0]); Assert.Equal(0, spanInt.FlattenedLength); - Assert.Equal(1, spanInt.Strides[0]); + Assert.Equal(0, spanInt.Strides[0]); // Make sure it still throws on index 0 Assert.Throws(() => { var spanInt = new TensorSpan(b); diff --git a/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs b/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs index db11389bd64c8c..f3e280f67587b3 100644 --- a/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs +++ b/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs @@ -618,6 +618,77 @@ public static void TensorFactoryCreateTests() }); } + [Fact] + public static void TensorCreateSingleElementTests() + { + // Tensor.Create with a single-element array should have stride 0 + Tensor src = Tensor.Create([1.0]); + Assert.Equal(1, src.Rank); + Assert.Equal(1, src.Lengths[0]); + Assert.Equal(0, src.Strides[0]); + Assert.Equal(1, src.FlattenedLength); + Assert.Equal(1.0, src[0]); + + // CreateFromShapeUninitialized without strides should work + Tensor dst = Tensor.CreateFromShapeUninitialized(src.Lengths); + Assert.Equal(1, dst.Rank); + Assert.Equal(1, dst.Lengths[0]); + Assert.Equal(0, dst.Strides[0]); + Assert.Equal(1, dst.FlattenedLength); + + // CopyTo should succeed + src.CopyTo(dst); + Assert.Equal(1.0, dst[0]); + + // CreateFromShapeUninitialized with explicit strides should work + dst = Tensor.CreateFromShapeUninitialized(src.Lengths, src.Strides); + Assert.Equal(1, dst.Rank); + Assert.Equal(1, dst.Lengths[0]); + Assert.Equal(0, dst.Strides[0]); + Assert.Equal(1, dst.FlattenedLength); + + src.CopyTo(dst); + Assert.Equal(1.0, dst[0]); + + // CreateFromShape without strides should also work + dst = Tensor.CreateFromShape(src.Lengths); + Assert.Equal(1, dst.Rank); + Assert.Equal(1, dst.Lengths[0]); + Assert.Equal(0, dst.Strides[0]); + Assert.Equal(1, dst.FlattenedLength); + + src.CopyTo(dst); + Assert.Equal(1.0, dst[0]); + + // CreateFromShape with explicit strides should also work + dst = Tensor.CreateFromShape(src.Lengths, src.Strides); + Assert.Equal(1, dst.Rank); + Assert.Equal(1, dst.Lengths[0]); + Assert.Equal(0, dst.Strides[0]); + Assert.Equal(1, dst.FlattenedLength); + + src.CopyTo(dst); + Assert.Equal(1.0, dst[0]); + + // TensorSpan from single-element span should also have stride 0 + Span span = [42.0]; + TensorSpan tensorSpan = new TensorSpan(span); + Assert.Equal(1, tensorSpan.Rank); + Assert.Equal(1, tensorSpan.Lengths[0]); + Assert.Equal(0, tensorSpan.Strides[0]); + Assert.Equal(1, tensorSpan.FlattenedLength); + Assert.Equal(42.0, tensorSpan[0]); + + // ReadOnlyTensorSpan from single-element span should also have stride 0 + ReadOnlySpan roSpan = [42.0]; + ReadOnlyTensorSpan roTensorSpan = new ReadOnlyTensorSpan(roSpan); + Assert.Equal(1, roTensorSpan.Rank); + Assert.Equal(1, roTensorSpan.Lengths[0]); + Assert.Equal(0, roTensorSpan.Strides[0]); + Assert.Equal(1, roTensorSpan.FlattenedLength); + Assert.Equal(42.0, roTensorSpan[0]); + } + [Fact] public static void TensorCosineSimilarityTests() { diff --git a/src/libraries/System.Private.CoreLib/ref/System.Private.CoreLib.csproj b/src/libraries/System.Private.CoreLib/ref/System.Private.CoreLib.csproj index 81fc70d1c7e359..b31ed92ff386d8 100644 --- a/src/libraries/System.Private.CoreLib/ref/System.Private.CoreLib.csproj +++ b/src/libraries/System.Private.CoreLib/ref/System.Private.CoreLib.csproj @@ -9,8 +9,7 @@ $(NoWarn);0809;0618;CS3015 SilverlightPlatform true - true - $(DefineConstants);FEATURE_WASM_MANAGED_THREADS + $(DefineConstants);FEATURE_WASM_MANAGED_THREADS $(DefineConstants);BUILDING_CORELIB_REFERENCE false diff --git a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs index 0ace31cf5ebb3b..725ef15dffd72e 100644 --- a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs @@ -38,8 +38,9 @@ public sealed partial class SafeFileHandle : SafeHandleZeroOrMinusOneIsInvalid || AppContextConfigHelper.GetBooleanConfig("System.IO.DisableFileLocking", "DOTNET_SYSTEM_IO_DISABLEFILELOCKING", defaultValue: false); // not using bool? as it's not thread safe - private volatile NullableBool _canSeek = NullableBool.Undefined; - private volatile NullableBool _supportsRandomAccess = NullableBool.Undefined; + private volatile NullableBool _canSeek /* = NullableBool.Undefined */; + private volatile NullableBool _supportsRandomAccess /* = NullableBool.Undefined */; + private volatile NullableBool _isAsync /* = NullableBool.Undefined */; private bool _deleteOnClose; private bool _isLocked; @@ -53,7 +54,25 @@ private SafeFileHandle(bool ownsHandle) SetHandle(new IntPtr(-1)); } - public bool IsAsync { get; private set; } + public bool IsAsync + { + get + { + NullableBool isAsync = _isAsync; + if (isAsync == NullableBool.Undefined && !IsClosed) + { + if (Interop.Sys.Fcntl.GetIsNonBlocking(this, out bool isNonBlocking) != 0) + { + throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), Path); + } + + _isAsync = isAsync = isNonBlocking ? NullableBool.True : NullableBool.False; + } + + return isAsync == NullableBool.True; + } + private set => _isAsync = value ? NullableBool.True : NullableBool.False; + } internal bool CanSeek => !IsClosed && GetCanSeek(); @@ -161,6 +180,49 @@ public override bool IsInvalid } } + public static partial void CreateAnonymousPipe(out SafeFileHandle readHandle, out SafeFileHandle writeHandle, bool asyncRead, bool asyncWrite) + { + // Allocate the handles first, so in case of OOM we don't leak any handles. + SafeFileHandle tempReadHandle = new(); + SafeFileHandle tempWriteHandle = new(); + + Interop.Sys.PipeFlags flags = Interop.Sys.PipeFlags.O_CLOEXEC; + if (asyncRead) + { + flags |= Interop.Sys.PipeFlags.O_NONBLOCK_READ; + } + + if (asyncWrite) + { + flags |= Interop.Sys.PipeFlags.O_NONBLOCK_WRITE; + } + + int readFd, writeFd; + unsafe + { + int* fds = stackalloc int[2]; + if (Interop.Sys.Pipe(fds, flags) != 0) + { + Interop.ErrorInfo error = Interop.Sys.GetLastErrorInfo(); + tempReadHandle.Dispose(); + tempWriteHandle.Dispose(); + throw Interop.GetExceptionForIoErrno(error); + } + + readFd = fds[Interop.Sys.ReadEndOfPipe]; + writeFd = fds[Interop.Sys.WriteEndOfPipe]; + } + + tempReadHandle.SetHandle(readFd); + tempReadHandle.IsAsync = asyncRead; + + tempWriteHandle.SetHandle(writeFd); + tempWriteHandle.IsAsync = asyncWrite; + + readHandle = tempReadHandle; + writeHandle = tempWriteHandle; + } + // Specialized Open that returns the file length and permissions of the opened file. // This information is retrieved from the 'stat' syscall that must be performed to ensure the path is not a directory. internal static SafeFileHandle OpenReadOnly(string fullPath, FileOptions options, out long fileLength, out UnixFileMode filePermissions) @@ -293,7 +355,7 @@ private static Interop.Sys.OpenFlags PreOpenConfigurationFromOptions(FileMode mo } // Translate some FileOptions; some just aren't supported, and others will be handled after calling open. - // - Asynchronous: Handled in ctor, setting _useAsync and SafeFileHandle.IsAsync to true + // - Asynchronous: Unix does not support O_NONBLOCK for regular files, only for pipes and sockets. // - DeleteOnClose: Doesn't have a Unix equivalent, but we approximate it in Dispose // - Encrypted: No equivalent on Unix and is ignored // - RandomAccess: Implemented after open if posix_fadvise is available @@ -346,7 +408,7 @@ private bool Init(string path, FileMode mode, FileAccess access, FileShare share filePermissions = ((UnixFileMode)status.Mode) & PermissionMask; } - IsAsync = (options & FileOptions.Asynchronous) != 0; + IsAsync = false; // Unix does not support O_NONBLOCK for regular files. // Lock the file if requested via FileShare. This is only advisory locking. FileShare.None implies an exclusive // lock on the file and all other modes use a shared lock. While this is not as granular as Windows, not mandatory, diff --git a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Windows.cs b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Windows.cs index 5589d4b9b74b54..3d5f8d22db02ba 100644 --- a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Windows.cs @@ -3,6 +3,7 @@ using System; using System.Buffers; +using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; @@ -21,6 +22,74 @@ public SafeFileHandle() : base(true) { } + public static partial void CreateAnonymousPipe(out SafeFileHandle readHandle, out SafeFileHandle writeHandle, bool asyncRead, bool asyncWrite) + { + Interop.Kernel32.SECURITY_ATTRIBUTES securityAttributes = default; + SafeFileHandle? tempReadHandle; + SafeFileHandle? tempWriteHandle; + + // When neither end is async, use the simple CreatePipe API + if (!asyncRead && !asyncWrite) + { + bool ret = Interop.Kernel32.CreatePipe(out tempReadHandle, out tempWriteHandle, ref securityAttributes, 0); + if (!ret) + { + throw new Win32Exception(); + } + + Debug.Assert(!tempReadHandle.IsInvalid); + Debug.Assert(!tempWriteHandle.IsInvalid); + + tempReadHandle._fileOptions = FileOptions.None; + tempWriteHandle._fileOptions = FileOptions.None; + } + else + { + // When one or both ends are async, use named pipes to support async I/O. + string pipeName = $@"\\.\pipe\dotnet_{Guid.NewGuid():N}"; + + // Security: we don't need to specify a security descriptor, because + // we allow only for 1 instance of the pipe and immediately open the write end, + // so there is no time window for another process to open the pipe with different permissions. + // Even if that happens, we are going to fail to open the write end and throw an exception, so there is no security risk. + + // Determine the open mode for the read end + int openMode = (int)Interop.Kernel32.PipeOptions.PIPE_ACCESS_INBOUND | + Interop.Kernel32.FileOperations.FILE_FLAG_FIRST_PIPE_INSTANCE; // Only one can be created with this name + + if (asyncRead) + { + openMode |= Interop.Kernel32.FileOperations.FILE_FLAG_OVERLAPPED; // Asynchronous I/O + } + + const int pipeMode = (int)(Interop.Kernel32.PipeOptions.PIPE_TYPE_BYTE | Interop.Kernel32.PipeOptions.PIPE_READMODE_BYTE); // Data is read from the pipe as a stream of bytes + + // We could consider specifying a larger buffer size. + tempReadHandle = Interop.Kernel32.CreateNamedPipeFileHandle(pipeName, openMode, pipeMode, 1, 0, 0, 0, ref securityAttributes); + + try + { + if (tempReadHandle.IsInvalid) + { + throw new Win32Exception(); + } + + tempReadHandle._fileOptions = asyncRead ? FileOptions.Asynchronous : FileOptions.None; + FileOptions writeOptions = asyncWrite ? FileOptions.Asynchronous : FileOptions.None; + tempWriteHandle = Open(pipeName, FileMode.Open, FileAccess.Write, FileShare.Read, writeOptions, preallocationSize: 0); + } + catch + { + tempReadHandle.Dispose(); + + throw; + } + } + + readHandle = tempReadHandle; + writeHandle = tempWriteHandle; + } + public bool IsAsync => (GetFileOptions() & FileOptions.Asynchronous) != 0; internal bool IsNoBuffering => (GetFileOptions() & NoBuffering) != 0; diff --git a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.cs b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.cs index 0a13a420cff8e0..81401661763eac 100644 --- a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.cs +++ b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.cs @@ -10,6 +10,24 @@ public sealed partial class SafeFileHandle : SafeHandleZeroOrMinusOneIsInvalid private string? _path; private volatile int _cachedFileType = -1; + /// + /// Creates an anonymous pipe. + /// + /// When this method returns, contains the read end of the pipe. + /// When this method returns, contains the write end of the pipe. + /// to enable asynchronous IO for the read end of the pipe; otherwise, . + /// to enable asynchronous IO for the write end of the pipe; otherwise, . + /// + /// + /// The created handles are not inheritable by design to avoid accidental handle leaks to child processes. + /// + /// + /// On Windows, async handles are created with the FILE_FLAG_OVERLAPPED flag. + /// On Unix, async handles are created with the O_NONBLOCK flag. + /// + /// + public static partial void CreateAnonymousPipe(out SafeFileHandle readHandle, out SafeFileHandle writeHandle, bool asyncRead = false, bool asyncWrite = false); + /// /// Creates a around a file handle. /// diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index f040846c83487b..a546bc4df703da 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -26,6 +26,7 @@ true true true + true $(DefineConstants);BIGENDIAN @@ -54,6 +55,8 @@ $(DefineConstants);FEATURE_CROSS_PROCESS_MUTEX + $(DefineConstants);FEATURE_MULTITHREADING + $(DefineConstants);FEATURE_WASM_MANAGED_THREADS @@ -1603,8 +1606,8 @@ - - + + @@ -1809,6 +1812,15 @@ Common\Interop\Windows\Kernel32\Interop.CreateFile.cs + + Common\Interop\Windows\Kernel32\Interop.PipeOptions.cs + + + Common\Interop\Windows\Kernel32\Interop.CreatePipe_SafeFileHandle.cs + + + Common\Interop\Windows\Kernel32\Interop.CreateNamedPipe_SafeFileHandle.cs + Common\Interop\Windows\Kernel32\Interop.EventWaitHandle.cs @@ -2410,6 +2422,9 @@ Common\Interop\Unix\System.Native\Interop.Close.cs + + Common\Interop\Unix\System.Native\Interop.Fcntl.cs + Common\Interop\Unix\System.Native\Interop.CopyFile.cs @@ -2434,6 +2449,9 @@ Common\Interop\Unix\System.Native\Interop.GetCpuUtilization.cs + + Common\Interop\Unix\System.Native\Interop.Pipe.cs + Common\Interop\Unix\System.Native\Interop.GetCwd.cs @@ -2847,14 +2865,14 @@ - + - - + + @@ -2865,19 +2883,19 @@ - + - + - + - + diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs index 147e3815812d0c..1fb4140feac36c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs @@ -30,7 +30,11 @@ internal static unsafe int ReadAtOffset(SafeFileHandle handle, Span buffer // isn't seekable. We do the same manually with PRead vs Read, in order to enable // the function to be used by FileStream for all the same situations. int result; - if (handle.SupportsRandomAccess) + if (handle.IsAsync) + { + result = Interop.Sys.ReadFromNonblocking(handle, bufPtr, buffer.Length); + } + else if (handle.SupportsRandomAccess) { // Try pread for seekable files. result = Interop.Sys.PRead(handle, bufPtr, buffer.Length, fileOffset); @@ -108,7 +112,11 @@ internal static unsafe void WriteAtOffset(SafeFileHandle handle, ReadOnlySpan false; -#else +#if FEATURE_MULTITHREADING => true; +#else + => false; #endif -#if FEATURE_SINGLE_THREADED - [DoesNotReturn] +#if FEATURE_WASM_MANAGED_THREADS internal static void ThrowIfMultithreadingIsNotSupported() { - throw new PlatformNotSupportedException(); + System.Threading.Thread.AssureBlockingPossible(); } -#elif FEATURE_WASM_MANAGED_THREADS +#elif !FEATURE_MULTITHREADING + [DoesNotReturn] internal static void ThrowIfMultithreadingIsNotSupported() { - System.Threading.Thread.AssureBlockingPossible(); + throw new PlatformNotSupportedException(); } #else [Conditional("unnecessary")] diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.PlatformNotSupported.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.PlatformNotSupported.cs index 819b72cec57008..6e013db76ae088 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.PlatformNotSupported.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.PlatformNotSupported.cs @@ -4938,18 +4938,6 @@ internal Arm64() { } // Load 32-bit data and zero-extend - /// - /// svint64_t svld1uw_gather_[s64]offset_s64(svbool_t pg, const uint32_t *base, svint64_t offsets) - /// LD1W Zresult.D, Pg/Z, [Xbase, Zoffsets.D] - /// - public static unsafe Vector GatherVectorUInt32WithByteOffsetsZeroExtend(Vector mask, uint* address, Vector offsets) { throw new PlatformNotSupportedException(); } - - /// - /// svint64_t svld1uw_gather_[u64]offset_s64(svbool_t pg, const uint32_t *base, svuint64_t offsets) - /// LD1W Zresult.D, Pg/Z, [Xbase, Zoffsets.D] - /// - public static unsafe Vector GatherVectorUInt32WithByteOffsetsZeroExtend(Vector mask, uint* address, Vector offsets) { throw new PlatformNotSupportedException(); } - /// /// svint64_t svld1uw_gather_[s64]offset_s64(svbool_t pg, const uint32_t *base, svint64_t offsets) /// LD1W Zresult.D, Pg/Z, [Xbase, Zoffsets.D] @@ -4962,18 +4950,6 @@ internal Arm64() { } /// public static unsafe Vector GatherVectorUInt32WithByteOffsetsZeroExtend(Vector mask, uint* address, Vector offsets) { throw new PlatformNotSupportedException(); } - /// - /// svuint64_t svld1uw_gather_[s64]offset_u64(svbool_t pg, const uint32_t *base, svint64_t offsets) - /// LD1W Zresult.D, Pg/Z, [Xbase, Zoffsets.D] - /// - public static unsafe Vector GatherVectorUInt32WithByteOffsetsZeroExtend(Vector mask, uint* address, Vector offsets) { throw new PlatformNotSupportedException(); } - - /// - /// svuint64_t svld1uw_gather_[u64]offset_u64(svbool_t pg, const uint32_t *base, svuint64_t offsets) - /// LD1W Zresult.D, Pg/Z, [Xbase, Zoffsets.D] - /// - public static unsafe Vector GatherVectorUInt32WithByteOffsetsZeroExtend(Vector mask, uint* address, Vector offsets) { throw new PlatformNotSupportedException(); } - /// /// svuint64_t svld1uw_gather_[s64]offset_u64(svbool_t pg, const uint32_t *base, svint64_t offsets) /// LD1W Zresult.D, Pg/Z, [Xbase, Zoffsets.D] @@ -4989,18 +4965,6 @@ internal Arm64() { } // Load 32-bit data and zero-extend, first-faulting - /// - /// svint64_t svldff1uw_gather_[s64]offset_s64(svbool_t pg, const uint32_t *base, svint64_t offsets) - /// LDFF1W Zresult.D, Pg/Z, [Xbase, Zoffsets.D] - /// - public static unsafe Vector GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting(Vector mask, uint* address, Vector offsets) { throw new PlatformNotSupportedException(); } - - /// - /// svint64_t svldff1uw_gather_[u64]offset_s64(svbool_t pg, const uint32_t *base, svuint64_t offsets) - /// LDFF1W Zresult.D, Pg/Z, [Xbase, Zoffsets.D] - /// - public static unsafe Vector GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting(Vector mask, uint* address, Vector offsets) { throw new PlatformNotSupportedException(); } - /// /// svint64_t svldff1uw_gather_[s64]offset_s64(svbool_t pg, const uint32_t *base, svint64_t offsets) /// LDFF1W Zresult.D, Pg/Z, [Xbase, Zoffsets.D] @@ -5013,18 +4977,6 @@ internal Arm64() { } /// public static unsafe Vector GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting(Vector mask, uint* address, Vector offsets) { throw new PlatformNotSupportedException(); } - /// - /// svuint64_t svldff1uw_gather_[s64]offset_u64(svbool_t pg, const uint32_t *base, svint64_t offsets) - /// LDFF1W Zresult.D, Pg/Z, [Xbase, Zoffsets.D] - /// - public static unsafe Vector GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting(Vector mask, uint* address, Vector offsets) { throw new PlatformNotSupportedException(); } - - /// - /// svuint64_t svldff1uw_gather_[u64]offset_u64(svbool_t pg, const uint32_t *base, svuint64_t offsets) - /// LDFF1W Zresult.D, Pg/Z, [Xbase, Zoffsets.D] - /// - public static unsafe Vector GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting(Vector mask, uint* address, Vector offsets) { throw new PlatformNotSupportedException(); } - /// /// svuint64_t svldff1uw_gather_[s64]offset_u64(svbool_t pg, const uint32_t *base, svint64_t offsets) /// LDFF1W Zresult.D, Pg/Z, [Xbase, Zoffsets.D] @@ -5040,25 +4992,6 @@ internal Arm64() { } // Load 32-bit data and zero-extend - /// - /// svint64_t svld1uw_gather_[s64]index_s64(svbool_t pg, const uint32_t *base, svint64_t indices) - /// LD1W Zresult.D, Pg/Z, [Xbase, Zindices.D, LSL #2] - /// - public static unsafe Vector GatherVectorUInt32ZeroExtend(Vector mask, uint* address, Vector indices) { throw new PlatformNotSupportedException(); } - - // - // svint64_t svld1uw_gather[_u64base]_s64(svbool_t pg, svuint64_t bases) - // LD1W Zresult.D, Pg/Z, [Zbases.D, #0] - // - // Removed as per #103297 - // public static Vector GatherVectorUInt32ZeroExtend(Vector mask, Vector addresses) { throw new PlatformNotSupportedException(); } - - /// - /// svint64_t svld1uw_gather_[u64]index_s64(svbool_t pg, const uint32_t *base, svuint64_t indices) - /// LD1W Zresult.D, Pg/Z, [Xbase, Zindices.D, LSL #2] - /// - public static unsafe Vector GatherVectorUInt32ZeroExtend(Vector mask, uint* address, Vector indices) { throw new PlatformNotSupportedException(); } - /// /// svint64_t svld1uw_gather_[s64]index_s64(svbool_t pg, const uint32_t *base, svint64_t indices) /// LD1W Zresult.D, Pg/Z, [Xbase, Zindices.D, LSL #2] @@ -5077,25 +5010,6 @@ internal Arm64() { } /// public static unsafe Vector GatherVectorUInt32ZeroExtend(Vector mask, uint* address, Vector indices) { throw new PlatformNotSupportedException(); } - /// - /// svuint64_t svld1uw_gather_[s64]index_u64(svbool_t pg, const uint32_t *base, svint64_t indices) - /// LD1W Zresult.D, Pg/Z, [Xbase, Zindices.D, LSL #2] - /// - public static unsafe Vector GatherVectorUInt32ZeroExtend(Vector mask, uint* address, Vector indices) { throw new PlatformNotSupportedException(); } - - // - // svuint64_t svld1uw_gather[_u64base]_u64(svbool_t pg, svuint64_t bases) - // LD1W Zresult.D, Pg/Z, [Zbases.D, #0] - // - // Removed as per #103297 - // public static Vector GatherVectorUInt32ZeroExtend(Vector mask, Vector addresses) { throw new PlatformNotSupportedException(); } - - /// - /// svuint64_t svld1uw_gather_[u64]index_u64(svbool_t pg, const uint32_t *base, svuint64_t indices) - /// LD1W Zresult.D, Pg/Z, [Xbase, Zindices.D, LSL #2] - /// - public static unsafe Vector GatherVectorUInt32ZeroExtend(Vector mask, uint* address, Vector indices) { throw new PlatformNotSupportedException(); } - /// /// svuint64_t svld1uw_gather_[s64]index_u64(svbool_t pg, const uint32_t *base, svint64_t indices) /// LD1W Zresult.D, Pg/Z, [Xbase, Zindices.D, LSL #2] @@ -5117,25 +5031,6 @@ internal Arm64() { } // Load 32-bit data and zero-extend, first-faulting - /// - /// svint64_t svldff1uw_gather_[s64]index_s64(svbool_t pg, const uint32_t *base, svint64_t indices) - /// LDFF1W Zresult.D, Pg/Z, [Xbase, Zindices.D, LSL #2] - /// - public static unsafe Vector GatherVectorUInt32ZeroExtendFirstFaulting(Vector mask, uint* address, Vector indices) { throw new PlatformNotSupportedException(); } - - // - // svint64_t svldff1uw_gather[_u64base]_s64(svbool_t pg, svuint64_t bases) - // LDFF1W Zresult.D, Pg/Z, [Zbases.D, #0] - // - // Removed as per #103297 - // public static Vector GatherVectorUInt32ZeroExtendFirstFaulting(Vector mask, Vector addresses) { throw new PlatformNotSupportedException(); } - - /// - /// svint64_t svldff1uw_gather_[u64]index_s64(svbool_t pg, const uint32_t *base, svuint64_t indices) - /// LDFF1W Zresult.D, Pg/Z, [Xbase, Zindices.D, LSL #2] - /// - public static unsafe Vector GatherVectorUInt32ZeroExtendFirstFaulting(Vector mask, uint* address, Vector indices) { throw new PlatformNotSupportedException(); } - /// /// svint64_t svldff1uw_gather_[s64]index_s64(svbool_t pg, const uint32_t *base, svint64_t indices) /// LDFF1W Zresult.D, Pg/Z, [Xbase, Zindices.D, LSL #2] @@ -5154,25 +5049,6 @@ internal Arm64() { } /// public static unsafe Vector GatherVectorUInt32ZeroExtendFirstFaulting(Vector mask, uint* address, Vector indices) { throw new PlatformNotSupportedException(); } - /// - /// svuint64_t svldff1uw_gather_[s64]index_u64(svbool_t pg, const uint32_t *base, svint64_t indices) - /// LDFF1W Zresult.D, Pg/Z, [Xbase, Zindices.D, LSL #2] - /// - public static unsafe Vector GatherVectorUInt32ZeroExtendFirstFaulting(Vector mask, uint* address, Vector indices) { throw new PlatformNotSupportedException(); } - - // - // svuint64_t svldff1uw_gather[_u64base]_u64(svbool_t pg, svuint64_t bases) - // LDFF1W Zresult.D, Pg/Z, [Zbases.D, #0] - // - // Removed as per #103297 - // public static Vector GatherVectorUInt32ZeroExtendFirstFaulting(Vector mask, Vector addresses) { throw new PlatformNotSupportedException(); } - - /// - /// svuint64_t svldff1uw_gather_[u64]index_u64(svbool_t pg, const uint32_t *base, svuint64_t indices) - /// LDFF1W Zresult.D, Pg/Z, [Xbase, Zindices.D, LSL #2] - /// - public static unsafe Vector GatherVectorUInt32ZeroExtendFirstFaulting(Vector mask, uint* address, Vector indices) { throw new PlatformNotSupportedException(); } - /// /// svuint64_t svldff1uw_gather_[s64]index_u64(svbool_t pg, const uint32_t *base, svint64_t indices) /// LDFF1W Zresult.D, Pg/Z, [Xbase, Zindices.D, LSL #2] diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.cs index 69d4375549fd7d..c6cf60093d09a4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.cs @@ -4935,18 +4935,6 @@ internal Arm64() { } // Load 32-bit data and zero-extend - /// - /// svint64_t svld1uw_gather_[s64]offset_s64(svbool_t pg, const uint32_t *base, svint64_t offsets) - /// LD1W Zresult.D, Pg/Z, [Xbase, Zoffsets.D] - /// - public static unsafe Vector GatherVectorUInt32WithByteOffsetsZeroExtend(Vector mask, uint* address, Vector offsets) => GatherVectorUInt32WithByteOffsetsZeroExtend(mask, address, offsets); - - /// - /// svint64_t svld1uw_gather_[u64]offset_s64(svbool_t pg, const uint32_t *base, svuint64_t offsets) - /// LD1W Zresult.D, Pg/Z, [Xbase, Zoffsets.D] - /// - public static unsafe Vector GatherVectorUInt32WithByteOffsetsZeroExtend(Vector mask, uint* address, Vector offsets) => GatherVectorUInt32WithByteOffsetsZeroExtend(mask, address, offsets); - /// /// svint64_t svld1uw_gather_[s64]offset_s64(svbool_t pg, const uint32_t *base, svint64_t offsets) /// LD1W Zresult.D, Pg/Z, [Xbase, Zoffsets.D] @@ -4959,18 +4947,6 @@ internal Arm64() { } /// public static unsafe Vector GatherVectorUInt32WithByteOffsetsZeroExtend(Vector mask, uint* address, Vector offsets) => GatherVectorUInt32WithByteOffsetsZeroExtend(mask, address, offsets); - /// - /// svuint64_t svld1uw_gather_[s64]offset_u64(svbool_t pg, const uint32_t *base, svint64_t offsets) - /// LD1W Zresult.D, Pg/Z, [Xbase, Zoffsets.D] - /// - public static unsafe Vector GatherVectorUInt32WithByteOffsetsZeroExtend(Vector mask, uint* address, Vector offsets) => GatherVectorUInt32WithByteOffsetsZeroExtend(mask, address, offsets); - - /// - /// svuint64_t svld1uw_gather_[u64]offset_u64(svbool_t pg, const uint32_t *base, svuint64_t offsets) - /// LD1W Zresult.D, Pg/Z, [Xbase, Zoffsets.D] - /// - public static unsafe Vector GatherVectorUInt32WithByteOffsetsZeroExtend(Vector mask, uint* address, Vector offsets) => GatherVectorUInt32WithByteOffsetsZeroExtend(mask, address, offsets); - /// /// svuint64_t svld1uw_gather_[s64]offset_u64(svbool_t pg, const uint32_t *base, svint64_t offsets) /// LD1W Zresult.D, Pg/Z, [Xbase, Zoffsets.D] @@ -4986,18 +4962,6 @@ internal Arm64() { } // Load 32-bit data and zero-extend, first-faulting - /// - /// svint64_t svldff1uw_gather_[s64]offset_s64(svbool_t pg, const uint32_t *base, svint64_t offsets) - /// LDFF1W Zresult.D, Pg/Z, [Xbase, Zoffsets.D] - /// - public static unsafe Vector GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting(Vector mask, uint* address, Vector offsets) => GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting(mask, address, offsets); - - /// - /// svint64_t svldff1uw_gather_[u64]offset_s64(svbool_t pg, const uint32_t *base, svuint64_t offsets) - /// LDFF1W Zresult.D, Pg/Z, [Xbase, Zoffsets.D] - /// - public static unsafe Vector GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting(Vector mask, uint* address, Vector offsets) => GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting(mask, address, offsets); - /// /// svint64_t svldff1uw_gather_[s64]offset_s64(svbool_t pg, const uint32_t *base, svint64_t offsets) /// LDFF1W Zresult.D, Pg/Z, [Xbase, Zoffsets.D] @@ -5010,18 +4974,6 @@ internal Arm64() { } /// public static unsafe Vector GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting(Vector mask, uint* address, Vector offsets) => GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting(mask, address, offsets); - /// - /// svuint64_t svldff1uw_gather_[s64]offset_u64(svbool_t pg, const uint32_t *base, svint64_t offsets) - /// LDFF1W Zresult.D, Pg/Z, [Xbase, Zoffsets.D] - /// - public static unsafe Vector GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting(Vector mask, uint* address, Vector offsets) => GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting(mask, address, offsets); - - /// - /// svuint64_t svldff1uw_gather_[u64]offset_u64(svbool_t pg, const uint32_t *base, svuint64_t offsets) - /// LDFF1W Zresult.D, Pg/Z, [Xbase, Zoffsets.D] - /// - public static unsafe Vector GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting(Vector mask, uint* address, Vector offsets) => GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting(mask, address, offsets); - /// /// svuint64_t svldff1uw_gather_[s64]offset_u64(svbool_t pg, const uint32_t *base, svint64_t offsets) /// LDFF1W Zresult.D, Pg/Z, [Xbase, Zoffsets.D] @@ -5037,25 +4989,6 @@ internal Arm64() { } // Load 32-bit data and zero-extend - /// - /// svint64_t svld1uw_gather_[s64]index_s64(svbool_t pg, const uint32_t *base, svint64_t indices) - /// LD1W Zresult.D, Pg/Z, [Xbase, Zindices.D, LSL #2] - /// - public static unsafe Vector GatherVectorUInt32ZeroExtend(Vector mask, uint* address, Vector indices) => GatherVectorUInt32ZeroExtend(mask, address, indices); - - // - // svint64_t svld1uw_gather[_u64base]_s64(svbool_t pg, svuint64_t bases) - // LD1W Zresult.D, Pg/Z, [Zbases.D, #0] - // - // Removed as per #103297 - // public static Vector GatherVectorUInt32ZeroExtend(Vector mask, Vector addresses) => GatherVectorUInt32ZeroExtend(mask, addresses); - - /// - /// svint64_t svld1uw_gather_[u64]index_s64(svbool_t pg, const uint32_t *base, svuint64_t indices) - /// LD1W Zresult.D, Pg/Z, [Xbase, Zindices.D, LSL #2] - /// - public static unsafe Vector GatherVectorUInt32ZeroExtend(Vector mask, uint* address, Vector indices) => GatherVectorUInt32ZeroExtend(mask, address, indices); - /// /// svint64_t svld1uw_gather_[s64]index_s64(svbool_t pg, const uint32_t *base, svint64_t indices) /// LD1W Zresult.D, Pg/Z, [Xbase, Zindices.D, LSL #2] @@ -5074,25 +5007,6 @@ internal Arm64() { } /// public static unsafe Vector GatherVectorUInt32ZeroExtend(Vector mask, uint* address, Vector indices) => GatherVectorUInt32ZeroExtend(mask, address, indices); - /// - /// svuint64_t svld1uw_gather_[s64]index_u64(svbool_t pg, const uint32_t *base, svint64_t indices) - /// LD1W Zresult.D, Pg/Z, [Xbase, Zindices.D, LSL #2] - /// - public static unsafe Vector GatherVectorUInt32ZeroExtend(Vector mask, uint* address, Vector indices) => GatherVectorUInt32ZeroExtend(mask, address, indices); - - // - // svuint64_t svld1uw_gather[_u64base]_u64(svbool_t pg, svuint64_t bases) - // LD1W Zresult.D, Pg/Z, [Zbases.D, #0] - // - // Removed as per #103297 - // public static Vector GatherVectorUInt32ZeroExtend(Vector mask, Vector addresses) => GatherVectorUInt32ZeroExtend(mask, addresses); - - /// - /// svuint64_t svld1uw_gather_[u64]index_u64(svbool_t pg, const uint32_t *base, svuint64_t indices) - /// LD1W Zresult.D, Pg/Z, [Xbase, Zindices.D, LSL #2] - /// - public static unsafe Vector GatherVectorUInt32ZeroExtend(Vector mask, uint* address, Vector indices) => GatherVectorUInt32ZeroExtend(mask, address, indices); - /// /// svuint64_t svld1uw_gather_[s64]index_u64(svbool_t pg, const uint32_t *base, svint64_t indices) /// LD1W Zresult.D, Pg/Z, [Xbase, Zindices.D, LSL #2] @@ -5114,25 +5028,6 @@ internal Arm64() { } // Load 32-bit data and zero-extend, first-faulting - /// - /// svint64_t svldff1uw_gather_[s64]index_s64(svbool_t pg, const uint32_t *base, svint64_t indices) - /// LDFF1W Zresult.D, Pg/Z, [Xbase, Zindices.D, LSL #2] - /// - public static unsafe Vector GatherVectorUInt32ZeroExtendFirstFaulting(Vector mask, uint* address, Vector indices) => GatherVectorUInt32ZeroExtendFirstFaulting(mask, address, indices); - - // - // svint64_t svldff1uw_gather[_u64base]_s64(svbool_t pg, svuint64_t bases) - // LDFF1W Zresult.D, Pg/Z, [Zbases.D, #0] - // - // Removed as per #103297 - // public static Vector GatherVectorUInt32ZeroExtendFirstFaulting(Vector mask, Vector addresses) => GatherVectorUInt32ZeroExtendFirstFaulting(mask, addresses); - - /// - /// svint64_t svldff1uw_gather_[u64]index_s64(svbool_t pg, const uint32_t *base, svuint64_t indices) - /// LDFF1W Zresult.D, Pg/Z, [Xbase, Zindices.D, LSL #2] - /// - public static unsafe Vector GatherVectorUInt32ZeroExtendFirstFaulting(Vector mask, uint* address, Vector indices) => GatherVectorUInt32ZeroExtendFirstFaulting(mask, address, indices); - /// /// svint64_t svldff1uw_gather_[s64]index_s64(svbool_t pg, const uint32_t *base, svint64_t indices) /// LDFF1W Zresult.D, Pg/Z, [Xbase, Zindices.D, LSL #2] @@ -5151,25 +5046,6 @@ internal Arm64() { } /// public static unsafe Vector GatherVectorUInt32ZeroExtendFirstFaulting(Vector mask, uint* address, Vector indices) => GatherVectorUInt32ZeroExtendFirstFaulting(mask, address, indices); - /// - /// svuint64_t svldff1uw_gather_[s64]index_u64(svbool_t pg, const uint32_t *base, svint64_t indices) - /// LDFF1W Zresult.D, Pg/Z, [Xbase, Zindices.D, LSL #2] - /// - public static unsafe Vector GatherVectorUInt32ZeroExtendFirstFaulting(Vector mask, uint* address, Vector indices) => GatherVectorUInt32ZeroExtendFirstFaulting(mask, address, indices); - - // - // svuint64_t svldff1uw_gather[_u64base]_u64(svbool_t pg, svuint64_t bases) - // LDFF1W Zresult.D, Pg/Z, [Zbases.D, #0] - // - // Removed as per #103297 - // public static Vector GatherVectorUInt32ZeroExtendFirstFaulting(Vector mask, Vector addresses) => GatherVectorUInt32ZeroExtendFirstFaulting(mask, addresses); - - /// - /// svuint64_t svldff1uw_gather_[u64]index_u64(svbool_t pg, const uint32_t *base, svuint64_t indices) - /// LDFF1W Zresult.D, Pg/Z, [Xbase, Zindices.D, LSL #2] - /// - public static unsafe Vector GatherVectorUInt32ZeroExtendFirstFaulting(Vector mask, uint* address, Vector indices) => GatherVectorUInt32ZeroExtendFirstFaulting(mask, address, indices); - /// /// svuint64_t svldff1uw_gather_[s64]index_u64(svbool_t pg, const uint32_t *base, svint64_t indices) /// LDFF1W Zresult.D, Pg/Z, [Xbase, Zindices.D, LSL #2] diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs index ba92e692eeac54..c994f3fbcc3f0d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs @@ -12,10 +12,10 @@ using System.Runtime.Versioning; using System.Threading.Tasks; -#if FEATURE_SINGLE_THREADED -using WorkQueue = System.Collections.Generic.Queue; -#else +#if FEATURE_MULTITHREADING using WorkQueue = System.Collections.Concurrent.ConcurrentQueue; +#else +using WorkQueue = System.Collections.Generic.Queue; #endif #if TARGET_WINDOWS using IOCompletionPollerEvent = System.Threading.PortableThreadPool.IOCompletionPoller.Event; @@ -715,10 +715,10 @@ internal static bool LocalFindAndPop(object callback) tl.isProcessingHighPriorityWorkItems = false; } -#if FEATURE_SINGLE_THREADED - else if (highPriorityWorkItems.Count == 0 && -#else +#if FEATURE_MULTITHREADING else if (!highPriorityWorkItems.IsEmpty && +#else + else if (highPriorityWorkItems.Count == 0 && #endif TryStartProcessingHighPriorityWorkItemsAndDequeue(tl, out workItem)) { diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj index 55ebce93258c36..839251733374e8 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj @@ -10,11 +10,10 @@ $([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) SR.SystemRuntimeInteropServicesJavaScript_PlatformNotSupported - true - true + true true false - $(DefineConstants);FEATURE_WASM_MANAGED_THREADS + $(DefineConstants);FEATURE_WASM_MANAGED_THREADS $(DefineConstants);ENABLE_JS_INTEROP_BY_VALUE $(DefineConstants);MONO $(DefineConstants);CORECLR @@ -71,8 +70,8 @@ - - + + diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System.Runtime.InteropServices.JavaScript.Tests.csproj b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System.Runtime.InteropServices.JavaScript.Tests.csproj index 3d87bce766315f..7da762fed4180d 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System.Runtime.InteropServices.JavaScript.Tests.csproj +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System.Runtime.InteropServices.JavaScript.Tests.csproj @@ -9,8 +9,7 @@ $(WasmXHarnessArgs) --engine-arg=--expose-gc --web-server-use-cop true true - true - $(DefineConstants);FEATURE_WASM_MANAGED_THREADS + $(DefineConstants);FEATURE_WASM_MANAGED_THREADS true 1 @@ -56,13 +55,13 @@ - + - + diff --git a/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.UnitTests/System/Runtime/InteropServices/NFloatTests.GenericMath.cs b/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.UnitTests/System/Runtime/InteropServices/NFloatTests.GenericMath.cs index 2d0b6b012a4599..a419ac4a601bfb 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.UnitTests/System/Runtime/InteropServices/NFloatTests.GenericMath.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.UnitTests/System/Runtime/InteropServices/NFloatTests.GenericMath.cs @@ -156,7 +156,7 @@ private static void AssertBitwiseEqual(NFloat expected, NFloat actual) return; } - throw Xunit.Sdk.EqualException.ForMismatchedValues(expected, actual); + throw Xunit.Sdk.EqualException.ForMismatchedValues(expected.ToString(), actual.ToString()); } // diff --git a/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs b/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs index 844074ad13facd..afb5c1bd6a59f2 100644 --- a/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs +++ b/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs @@ -5308,43 +5308,23 @@ internal Arm64() { } public static unsafe System.Numerics.Vector GatherVectorUInt16ZeroExtendFirstFaulting(System.Numerics.Vector mask, ushort* address, System.Numerics.Vector indices) { throw null; } public static System.Numerics.Vector GatherVectorUInt16ZeroExtendFirstFaulting(System.Numerics.Vector mask, System.Numerics.Vector addresses) { throw null; } public static unsafe System.Numerics.Vector GatherVectorUInt16ZeroExtendFirstFaulting(System.Numerics.Vector mask, ushort* address, System.Numerics.Vector indices) { throw null; } - public static unsafe System.Numerics.Vector GatherVectorUInt32WithByteOffsetsZeroExtend(System.Numerics.Vector mask, uint* address, System.Numerics.Vector offsets) { throw null; } - public static unsafe System.Numerics.Vector GatherVectorUInt32WithByteOffsetsZeroExtend(System.Numerics.Vector mask, uint* address, System.Numerics.Vector offsets) { throw null; } public static unsafe System.Numerics.Vector GatherVectorUInt32WithByteOffsetsZeroExtend(System.Numerics.Vector mask, uint* address, System.Numerics.Vector offsets) { throw null; } public static unsafe System.Numerics.Vector GatherVectorUInt32WithByteOffsetsZeroExtend(System.Numerics.Vector mask, uint* address, System.Numerics.Vector offsets) { throw null; } - public static unsafe System.Numerics.Vector GatherVectorUInt32WithByteOffsetsZeroExtend(System.Numerics.Vector mask, uint* address, System.Numerics.Vector offsets) { throw null; } - public static unsafe System.Numerics.Vector GatherVectorUInt32WithByteOffsetsZeroExtend(System.Numerics.Vector mask, uint* address, System.Numerics.Vector offsets) { throw null; } public static unsafe System.Numerics.Vector GatherVectorUInt32WithByteOffsetsZeroExtend(System.Numerics.Vector mask, uint* address, System.Numerics.Vector offsets) { throw null; } public static unsafe System.Numerics.Vector GatherVectorUInt32WithByteOffsetsZeroExtend(System.Numerics.Vector mask, uint* address, System.Numerics.Vector offsets) { throw null; } - public static unsafe System.Numerics.Vector GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting(System.Numerics.Vector mask, uint* address, System.Numerics.Vector offsets) { throw null; } - public static unsafe System.Numerics.Vector GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting(System.Numerics.Vector mask, uint* address, System.Numerics.Vector offsets) { throw null; } public static unsafe System.Numerics.Vector GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting(System.Numerics.Vector mask, uint* address, System.Numerics.Vector offsets) { throw null; } public static unsafe System.Numerics.Vector GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting(System.Numerics.Vector mask, uint* address, System.Numerics.Vector offsets) { throw null; } - public static unsafe System.Numerics.Vector GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting(System.Numerics.Vector mask, uint* address, System.Numerics.Vector offsets) { throw null; } - public static unsafe System.Numerics.Vector GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting(System.Numerics.Vector mask, uint* address, System.Numerics.Vector offsets) { throw null; } public static unsafe System.Numerics.Vector GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting(System.Numerics.Vector mask, uint* address, System.Numerics.Vector offsets) { throw null; } public static unsafe System.Numerics.Vector GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting(System.Numerics.Vector mask, uint* address, System.Numerics.Vector offsets) { throw null; } - public static unsafe System.Numerics.Vector GatherVectorUInt32ZeroExtend(System.Numerics.Vector mask, uint* address, System.Numerics.Vector indices) { throw null; } - // public static System.Numerics.Vector GatherVectorUInt32ZeroExtend(System.Numerics.Vector mask, System.Numerics.Vector addresses) { throw null; } - public static unsafe System.Numerics.Vector GatherVectorUInt32ZeroExtend(System.Numerics.Vector mask, uint* address, System.Numerics.Vector indices) { throw null; } public static unsafe System.Numerics.Vector GatherVectorUInt32ZeroExtend(System.Numerics.Vector mask, uint* address, System.Numerics.Vector indices) { throw null; } public static System.Numerics.Vector GatherVectorUInt32ZeroExtend(System.Numerics.Vector mask, System.Numerics.Vector addresses) { throw null; } public static unsafe System.Numerics.Vector GatherVectorUInt32ZeroExtend(System.Numerics.Vector mask, uint* address, System.Numerics.Vector indices) { throw null; } - public static unsafe System.Numerics.Vector GatherVectorUInt32ZeroExtend(System.Numerics.Vector mask, uint* address, System.Numerics.Vector indices) { throw null; } - // public static System.Numerics.Vector GatherVectorUInt32ZeroExtend(System.Numerics.Vector mask, System.Numerics.Vector addresses) { throw null; } - public static unsafe System.Numerics.Vector GatherVectorUInt32ZeroExtend(System.Numerics.Vector mask, uint* address, System.Numerics.Vector indices) { throw null; } public static unsafe System.Numerics.Vector GatherVectorUInt32ZeroExtend(System.Numerics.Vector mask, uint* address, System.Numerics.Vector indices) { throw null; } public static System.Numerics.Vector GatherVectorUInt32ZeroExtend(System.Numerics.Vector mask, System.Numerics.Vector addresses) { throw null; } public static unsafe System.Numerics.Vector GatherVectorUInt32ZeroExtend(System.Numerics.Vector mask, uint* address, System.Numerics.Vector indices) { throw null; } - public static unsafe System.Numerics.Vector GatherVectorUInt32ZeroExtendFirstFaulting(System.Numerics.Vector mask, uint* address, System.Numerics.Vector indices) { throw null; } - // public static System.Numerics.Vector GatherVectorUInt32ZeroExtendFirstFaulting(System.Numerics.Vector mask, System.Numerics.Vector addresses) { throw null; } - public static unsafe System.Numerics.Vector GatherVectorUInt32ZeroExtendFirstFaulting(System.Numerics.Vector mask, uint* address, System.Numerics.Vector indices) { throw null; } public static unsafe System.Numerics.Vector GatherVectorUInt32ZeroExtendFirstFaulting(System.Numerics.Vector mask, uint* address, System.Numerics.Vector indices) { throw null; } public static System.Numerics.Vector GatherVectorUInt32ZeroExtendFirstFaulting(System.Numerics.Vector mask, System.Numerics.Vector addresses) { throw null; } public static unsafe System.Numerics.Vector GatherVectorUInt32ZeroExtendFirstFaulting(System.Numerics.Vector mask, uint* address, System.Numerics.Vector indices) { throw null; } - public static unsafe System.Numerics.Vector GatherVectorUInt32ZeroExtendFirstFaulting(System.Numerics.Vector mask, uint* address, System.Numerics.Vector indices) { throw null; } - // public static System.Numerics.Vector GatherVectorUInt32ZeroExtendFirstFaulting(System.Numerics.Vector mask, System.Numerics.Vector addresses) { throw null; } - public static unsafe System.Numerics.Vector GatherVectorUInt32ZeroExtendFirstFaulting(System.Numerics.Vector mask, uint* address, System.Numerics.Vector indices) { throw null; } public static unsafe System.Numerics.Vector GatherVectorUInt32ZeroExtendFirstFaulting(System.Numerics.Vector mask, uint* address, System.Numerics.Vector indices) { throw null; } public static System.Numerics.Vector GatherVectorUInt32ZeroExtendFirstFaulting(System.Numerics.Vector mask, System.Numerics.Vector addresses) { throw null; } public static unsafe System.Numerics.Vector GatherVectorUInt32ZeroExtendFirstFaulting(System.Numerics.Vector mask, uint* address, System.Numerics.Vector indices) { throw null; } diff --git a/src/libraries/System.Runtime.Numerics/tests/ComplexTests.GenericMath.cs b/src/libraries/System.Runtime.Numerics/tests/ComplexTests.GenericMath.cs index 2982cd084b0230..14071a58617fb0 100644 --- a/src/libraries/System.Runtime.Numerics/tests/ComplexTests.GenericMath.cs +++ b/src/libraries/System.Runtime.Numerics/tests/ComplexTests.GenericMath.cs @@ -27,7 +27,7 @@ private static void AssertBitwiseEqual(double expected, double actual) return; } - throw Xunit.Sdk.EqualException.ForMismatchedValues(expected, actual); + throw Xunit.Sdk.EqualException.ForMismatchedValues(expected.ToString(), actual.ToString()); } private static void AssertBitwiseEqual(Complex expected, Complex actual) diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 63b0c03545ff9a..ccc217672ebad4 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -18,6 +18,7 @@ protected CriticalHandleZeroOrMinusOneIsInvalid() : base (default(System.IntPtr) } public sealed partial class SafeFileHandle : Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid { + public static void CreateAnonymousPipe(out Microsoft.Win32.SafeHandles.SafeFileHandle readHandle, out Microsoft.Win32.SafeHandles.SafeFileHandle writeHandle, bool asyncRead = false, bool asyncWrite = false) { throw null; } public SafeFileHandle() : base (default(bool)) { } public SafeFileHandle(System.IntPtr preexistingHandle, bool ownsHandle) : base (default(bool)) { } public override bool IsInvalid { get { throw null; } } diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Tests/System/Globalization/CharUnicodeInfoTests.Generated.cs b/src/libraries/System.Runtime/tests/System.Globalization.Tests/System/Globalization/CharUnicodeInfoTests.Generated.cs index 24f09fe0e55e3a..77fc787a294f5e 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Tests/System/Globalization/CharUnicodeInfoTests.Generated.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Tests/System/Globalization/CharUnicodeInfoTests.Generated.cs @@ -136,8 +136,8 @@ private static void AssertEqual(T expected, T actual, string methodName, Code if (!EqualityComparer.Default.Equals(expected, actual)) { throw EqualException.ForMismatchedValues( - expected: expected, - actual: actual, + expected: $"{expected}", + actual: $"{actual}", banner: FormattableString.Invariant($"CharUnicodeInfo.{methodName}({codePoint}) returned unexpected value.")); } } diff --git a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/Directory/Delete_MountVolume.cs b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/Directory/Delete_MountVolume.cs index f4b153d94d9326..ed74831de984f6 100644 --- a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/Directory/Delete_MountVolume.cs +++ b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/Directory/Delete_MountVolume.cs @@ -79,8 +79,11 @@ public static void RunTest() } finally { - MountHelper.Unmount(mountedDirName); - DeleteDir(mountedDirName, true); + if (Directory.Exists(mountedDirName)) + { + MountHelper.Unmount(mountedDirName); + DeleteDir(mountedDirName, true); + } } File.AppendAllText(debugFileName, string.Format("Completed scenario {0}", Environment.NewLine)); } @@ -124,8 +127,11 @@ public static void RunTest() } finally { - MountHelper.Unmount(mountedDirName); - DeleteDir(mountedDirName, true); + if (Directory.Exists(mountedDirName)) + { + MountHelper.Unmount(mountedDirName); + DeleteDir(mountedDirName, true); + } } File.AppendAllText(debugFileName, string.Format("Completed scenario {0}", Environment.NewLine)); } @@ -166,8 +172,11 @@ public static void RunTest() } finally { - MountHelper.Unmount(mountedDirName); - DeleteDir(mountedDirName, true); + if (Directory.Exists(mountedDirName)) + { + MountHelper.Unmount(mountedDirName); + DeleteDir(mountedDirName, true); + } } File.AppendAllText(debugFileName, string.Format("Completed scenario {0}", Environment.NewLine)); } @@ -208,8 +217,11 @@ public static void RunTest() } finally { - MountHelper.Unmount(mountedDirName); - DeleteDir(mountedDirName, true); + if (Directory.Exists(mountedDirName)) + { + MountHelper.Unmount(mountedDirName); + DeleteDir(mountedDirName, true); + } } File.AppendAllText(debugFileName, string.Format("Completed scenario {0}", Environment.NewLine)); } diff --git a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/File/OpenHandle.cs b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/File/OpenHandle.cs index 3d7bcb303b4ce7..5b2bd244570c57 100644 --- a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/File/OpenHandle.cs +++ b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/File/OpenHandle.cs @@ -66,15 +66,15 @@ public override void FileModeAppendExisting(string streamSpecifier) [Theory] [InlineData(FileOptions.None)] [InlineData(FileOptions.Asynchronous)] - public void SafeFileHandle_IsAsync_ReturnsCorrectInformation(FileOptions options) + public void SafeFileHandle_IsAsync_ReturnsCorrectInformation_ForRegularFiles(FileOptions options) { using (var handle = File.OpenHandle(GetTestFilePath(), FileMode.Create, FileAccess.Write, options: options)) { - Assert.Equal((options & FileOptions.Asynchronous) != 0, handle.IsAsync); + Assert.Equal((options & FileOptions.Asynchronous) != 0 && IsAsyncIoSupportedForRegularFiles, handle.IsAsync); // the following code exercises the code path where we don't know FileOptions used for opening the handle // and instead we ask the OS about it - if (OperatingSystem.IsWindows()) // async file handles are a Windows concept + if (IsAsyncIoSupportedForRegularFiles) { SafeFileHandle createdFromIntPtr = new SafeFileHandle(handle.DangerousGetHandle(), ownsHandle: false); Assert.Equal((options & FileOptions.Asynchronous) != 0, createdFromIntPtr.IsAsync); @@ -82,6 +82,33 @@ public void SafeFileHandle_IsAsync_ReturnsCorrectInformation(FileOptions options } } + [Theory] + [InlineData(true)] + [InlineData(false)] + [SkipOnPlatform(TestPlatforms.Browser, "Pipes are not supported on browser")] + public void SafeFileHandle_IsAsync_ReturnsCorrectInformation_ForPipes(bool useAsync) + { + SafeFileHandle.CreateAnonymousPipe(out SafeFileHandle readHandle, out SafeFileHandle writeHandle, asyncRead: useAsync, asyncWrite: useAsync); + + using (readHandle) + using (writeHandle) + { + Verify(readHandle, useAsync); + Verify(writeHandle, useAsync); + } + + static void Verify(SafeFileHandle fileHandle, bool useAsyncIO) + { + Assert.Equal(useAsyncIO, fileHandle.IsAsync); + Assert.Equal(FileHandleType.Pipe, fileHandle.Type); + + // The following code exercises the code path where the information is fetched from OS. + using SafeFileHandle createdFromIntPtr = new(fileHandle.DangerousGetHandle(), ownsHandle: false); + Assert.Equal(useAsyncIO, createdFromIntPtr.IsAsync); + Assert.Equal(FileHandleType.Pipe, createdFromIntPtr.Type); + } + } + [Theory] [InlineData(FileOptions.DeleteOnClose)] [InlineData(FileOptions.DeleteOnClose | FileOptions.Asynchronous)] diff --git a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileStream/FileStreamConformanceTests.cs b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileStream/FileStreamConformanceTests.cs index 812bb775f69687..f02846332251d0 100644 --- a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileStream/FileStreamConformanceTests.cs +++ b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileStream/FileStreamConformanceTests.cs @@ -246,6 +246,46 @@ protected override Task CreateConnectedStreamsAsync() protected override bool SupportsConcurrentBidirectionalUse => false; } + public abstract class AnonymousPipeFileStream_SafeFileHandle_CreateAnonymousPipe : AnonymousPipeFileStreamConnectedConformanceTests + { + protected abstract bool AsyncReads { get; } + protected abstract bool AsyncWrites { get; } + + protected override Task CreateConnectedStreamsAsync() + { + SafeFileHandle.CreateAnonymousPipe(out SafeFileHandle readHandle, out SafeFileHandle writeHandle, asyncRead: AsyncReads, asyncWrite: AsyncWrites); + + FileStream writeStream = new(writeHandle, FileAccess.Write); + FileStream readStream = new(readHandle, FileAccess.Read); + + return Task.FromResult((writeStream, readStream)); + } + } + + public class AnonymousPipeFileStreamConnectedConformanceTests_SyncRead_SyncWrite : AnonymousPipeFileStream_SafeFileHandle_CreateAnonymousPipe + { + protected override bool AsyncReads => false; + protected override bool AsyncWrites => false; + } + + public class AnonymousPipeFileStreamConnectedConformanceTests_AsyncRead_SyncWrite : AnonymousPipeFileStream_SafeFileHandle_CreateAnonymousPipe + { + protected override bool AsyncReads => true; + protected override bool AsyncWrites => false; + } + + public class AnonymousPipeFileStreamConnectedConformanceTests_SyncRead_AsyncWrite : AnonymousPipeFileStream_SafeFileHandle_CreateAnonymousPipe + { + protected override bool AsyncReads => false; + protected override bool AsyncWrites => true; + } + + public class AnonymousPipeFileStreamConnectedConformanceTests_AsyncRead_AsyncWrite : AnonymousPipeFileStream_SafeFileHandle_CreateAnonymousPipe + { + protected override bool AsyncReads => true; + protected override bool AsyncWrites => true; + } + public class NamedPipeFileStreamConnectedConformanceTests : ConnectedStreamConformanceTests { protected override async Task CreateConnectedStreamsAsync() diff --git a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileStream/FileStreamOptions.cs b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileStream/FileStreamOptions.cs index 5155fa41c490bd..3c13c634b5b0df 100644 --- a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileStream/FileStreamOptions.cs +++ b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileStream/FileStreamOptions.cs @@ -192,7 +192,7 @@ static void Validate(FileStream fs, string expectedPath, bool expectedAsync, boo using (fs) { Assert.Equal(expectedPath, fs.Name); - Assert.Equal(expectedAsync, fs.IsAsync); + Assert.Equal(expectedAsync && IsAsyncIoSupportedForRegularFiles, fs.IsAsync); Assert.Equal(expectedCanRead, fs.CanRead); Assert.Equal(expectedCanWrite, fs.CanWrite); } diff --git a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileStream/IsAsync.cs b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileStream/IsAsync.cs index e22428c4b39780..30e21b98de7510 100644 --- a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileStream/IsAsync.cs +++ b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileStream/IsAsync.cs @@ -15,7 +15,7 @@ public void IsAsyncConstructorArg() { using (FileStream fs = new FileStream(GetTestFilePath(), FileMode.Create, FileAccess.ReadWrite, FileShare.Read, 4096, true)) { - Assert.True(fs.IsAsync); + Assert.Equal(IsAsyncIoSupportedForRegularFiles, fs.IsAsync); } using (FileStream fs = new FileStream(GetTestFilePath(), FileMode.Create, FileAccess.ReadWrite, FileShare.Read, 4096, false)) @@ -29,7 +29,7 @@ public void FileOptionsAsynchronousConstructorArg() { using (FileStream fs = new FileStream(GetTestFilePath(), FileMode.Create, FileAccess.ReadWrite, FileShare.Read, 4096, FileOptions.Asynchronous)) { - Assert.True(fs.IsAsync); + Assert.Equal(IsAsyncIoSupportedForRegularFiles, fs.IsAsync); } using (FileStream fs = new FileStream(GetTestFilePath(), FileMode.Create, FileAccess.ReadWrite, FileShare.Read, 4096, FileOptions.None)) @@ -44,7 +44,7 @@ public void AsyncDiscoveredFromHandle() using (FileStream fs = new FileStream(GetTestFilePath(), FileMode.Create, FileAccess.ReadWrite, FileShare.Read, 4096, true)) using (FileStream fsh = new FileStream(fs.SafeFileHandle, FileAccess.ReadWrite)) { - Assert.True(fsh.IsAsync); + Assert.Equal(IsAsyncIoSupportedForRegularFiles, fsh.IsAsync); } using (FileStream fs = new FileStream(GetTestFilePath(), FileMode.Create, FileAccess.ReadWrite, FileShare.Read, 4096, false)) diff --git a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileStream/ctor_sfh_fa_buffer_async.cs b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileStream/ctor_sfh_fa_buffer_async.cs index 665bf08dbb3892..256129d8c3a7e0 100644 --- a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileStream/ctor_sfh_fa_buffer_async.cs +++ b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileStream/ctor_sfh_fa_buffer_async.cs @@ -40,7 +40,7 @@ public async Task UnmatchedAsyncIsAllowed(bool isAsync) using (FileStream newFs = CreateFileStream(fs.SafeFileHandle, FileAccess.ReadWrite, 4096, !isAsync)) { // Verify that the new FileStream uses handle's IsAsync, not the parameter - Assert.Equal(isAsync, newFs.IsAsync); + Assert.Equal(IsAsyncIoSupportedForRegularFiles && isAsync, newFs.IsAsync); // Perform async write, seek to beginning, and async read to verify functionality byte[] writeBuffer = new byte[] { 1, 2, 3, 4, 5 }; diff --git a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileStream/ctor_str_fm_fa_fs_buffer_async.cs b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileStream/ctor_str_fm_fa_fs_buffer_async.cs index ee4036a312fc5a..0ab7a860d9d9cd 100644 --- a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileStream/ctor_str_fm_fa_fs_buffer_async.cs +++ b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileStream/ctor_str_fm_fa_fs_buffer_async.cs @@ -24,7 +24,7 @@ public void ValidUseAsync(bool isAsync) { using (FileStream fs = CreateFileStream(GetTestFilePath(), FileMode.Create, FileAccess.ReadWrite, FileShare.Read, c_DefaultBufferSize, isAsync)) { - Assert.Equal(isAsync, fs.IsAsync); + Assert.Equal(IsAsyncIoSupportedForRegularFiles && isAsync, fs.IsAsync); } } } diff --git a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileStream/ctor_str_fm_fa_fs_buffer_fo.cs b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileStream/ctor_str_fm_fa_fs_buffer_fo.cs index 54a7d3043472ba..9fc1a850666f24 100644 --- a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileStream/ctor_str_fm_fa_fs_buffer_fo.cs +++ b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileStream/ctor_str_fm_fa_fs_buffer_fo.cs @@ -49,7 +49,7 @@ public void ValidFileOptions(FileOptions option) using (FileStream fs = CreateFileStream(GetTestFilePath(), FileMode.Create, FileAccess.ReadWrite, FileShare.Read, c_DefaultBufferSize, option)) { - Assert.Equal((option & FileOptions.Asynchronous) != 0, fs.IsAsync); + Assert.Equal((option & FileOptions.Asynchronous) != 0 && IsAsyncIoSupportedForRegularFiles, fs.IsAsync); // make sure we can write, seek, and read data with this option set fs.Write(data, 0, data.Length); diff --git a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileSystemTest.cs b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileSystemTest.cs index b2db26f0b50c4f..88e75df34c7333 100644 --- a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileSystemTest.cs +++ b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileSystemTest.cs @@ -13,6 +13,8 @@ public abstract partial class FileSystemTest : FileCleanupTestBase public static bool ReservedDeviceNamesAreBlocked => PlatformDetection.IsWindows && !PlatformDetection.IsWindows10OrLater; + public static bool IsAsyncIoSupportedForRegularFiles => PlatformDetection.IsWindows; + public static TheoryData PathsWithInvalidColons = TestData.PathsWithInvalidColons; public static TheoryData PathsWithInvalidCharacters = TestData.PathsWithInvalidCharacters; public static TheoryData TrailingCharacters = TestData.TrailingCharacters; diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/CharTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/CharTests.cs index 77854eed48bb30..4e2c69da5b2cf2 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/CharTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/CharTests.cs @@ -1343,8 +1343,8 @@ public static void GetUnicodeCategory_Char_AllInputs() { // We'll build up the exception message ourselves so the dev knows what code point failed. throw EqualException.ForMismatchedValues( - expected: expected, - actual: char.GetUnicodeCategory((char)i), + expected: expected.ToString(), + actual: char.GetUnicodeCategory((char)i).ToString(), banner: FormattableString.Invariant($@"char.GetUnicodeCategory('\u{i:X4}') returned wrong value.")); } } @@ -1363,8 +1363,8 @@ public static void IsLetter_Char_AllInputs() { // We'll build up the exception message ourselves so the dev knows what code point failed. throw EqualException.ForMismatchedValues( - expected: UnicodeData.IsLetter((char)i), - actual: char.IsLetter((char)i), + expected: UnicodeData.IsLetter((char)i).ToString(), + actual: char.IsLetter((char)i).ToString(), banner: FormattableString.Invariant($@"char.IsLetter('\u{i:X4}') returned wrong value.")); } } @@ -1384,8 +1384,8 @@ public static void IsLower_Char_AllInputs() { // We'll build up the exception message ourselves so the dev knows what code point failed. throw EqualException.ForMismatchedValues( - expected: expected, - actual: char.IsLower((char)i), + expected: expected.ToString(), + actual: char.IsLower((char)i).ToString(), banner: FormattableString.Invariant($@"char.IsLower('\u{i:X4}') returned wrong value.")); } } @@ -1406,8 +1406,8 @@ public static void IsUpper_Char_AllInputs() { // We'll build up the exception message ourselves so the dev knows what code point failed. throw EqualException.ForMismatchedValues( - expected: expected, - actual: char.IsUpper((char)i), + expected: expected.ToString(), + actual: char.IsUpper((char)i).ToString(), banner: FormattableString.Invariant($@"char.IsUpper('\u{i:X4}') returned wrong value.")); } } @@ -1426,8 +1426,8 @@ public static void IsWhiteSpace_Char_AllInputs() { // We'll build up the exception message ourselves so the dev knows what code point failed. throw EqualException.ForMismatchedValues( - expected: UnicodeData.IsWhiteSpace(i), - actual: char.IsWhiteSpace((char)i), + expected: UnicodeData.IsWhiteSpace(i).ToString(), + actual: char.IsWhiteSpace((char)i).ToString(), banner: FormattableString.Invariant($@"char.IsWhiteSpace('\u{i:X4}') returned wrong value.")); } } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DecimalTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DecimalTests.cs index 6363e20f02e313..3c48cbd64cd202 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DecimalTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DecimalTests.cs @@ -1731,7 +1731,7 @@ public static void Test() int expected = b1.CompareTo(bigDecimals[j]); int actual = d1.CompareTo(d2); if (expected != actual) - throw Xunit.Sdk.EqualException.ForMismatchedValues(expected, actual, d1 + " CMP " + d2); + throw Xunit.Sdk.EqualException.ForMismatchedValues(expected.ToString(), actual.ToString(), d1 + " CMP " + d2); } } } @@ -1759,7 +1759,7 @@ public static void Test() try { decimal actual = d1 + d2; - throw Xunit.Sdk.EqualException.ForMismatchedValues(typeof(OverflowException), actual, d1 + " + " + d2); + throw Xunit.Sdk.EqualException.ForMismatchedValues(typeof(OverflowException).ToString(), actual.ToString(), d1 + " + " + d2); } catch (OverflowException) { } } @@ -1768,7 +1768,7 @@ public static void Test() { decimal actual = d1 + d2; if (expected.Scale != (byte)(*(uint*)&actual >> BigDecimal.ScaleShift) || expected.CompareTo(new BigDecimal(actual)) != 0) - throw Xunit.Sdk.EqualException.ForMismatchedValues(expected, actual, d1 + " + " + d2); + throw Xunit.Sdk.EqualException.ForMismatchedValues(expected.ToString(), actual.ToString(), d1 + " + " + d2); } } } @@ -1797,7 +1797,7 @@ public static void Test() try { decimal actual = d1 * d2; - throw Xunit.Sdk.EqualException.ForMismatchedValues(typeof(OverflowException), actual, d1 + " * " + d2); + throw Xunit.Sdk.EqualException.ForMismatchedValues(typeof(OverflowException).ToString(), actual.ToString(), d1 + " * " + d2); } catch (OverflowException) { } } @@ -1806,7 +1806,7 @@ public static void Test() { decimal actual = d1 * d2; if (expected.Scale != (byte)(*(uint*)&actual >> BigDecimal.ScaleShift) || expected.CompareTo(new BigDecimal(actual)) != 0) - throw Xunit.Sdk.EqualException.ForMismatchedValues(expected, actual, d1 + " * " + d2); + throw Xunit.Sdk.EqualException.ForMismatchedValues(expected.ToString(), actual.ToString(), d1 + " * " + d2); } } } @@ -1837,7 +1837,7 @@ public static void Test() try { decimal actual = d1 / d2; - throw Xunit.Sdk.EqualException.ForMismatchedValues(typeof(OverflowException), actual, d1 + " / " + d2); + throw Xunit.Sdk.EqualException.ForMismatchedValues(typeof(OverflowException).ToString(), actual.ToString(), d1 + " / " + d2); } catch (OverflowException) { } } @@ -1846,7 +1846,7 @@ public static void Test() { decimal actual = d1 / d2; if (expected.Scale != (byte)(*(uint*)&actual >> BigDecimal.ScaleShift) || expected.CompareTo(new BigDecimal(actual)) != 0) - throw Xunit.Sdk.EqualException.ForMismatchedValues(expected, actual, d1 + " / " + d2); + throw Xunit.Sdk.EqualException.ForMismatchedValues(expected.ToString(), actual.ToString(), d1 + " / " + d2); } } } @@ -1875,12 +1875,12 @@ public static void Test() unsafe { if (expected.Scale != (byte)(*(uint*)&actual >> BigDecimal.ScaleShift) || expected.CompareTo(new BigDecimal(actual)) != 0) - throw Xunit.Sdk.EqualException.ForMismatchedValues(expected, actual, d1 + " % " + d2); + throw Xunit.Sdk.EqualException.ForMismatchedValues(expected.ToString(), actual.ToString(), d1 + " % " + d2); } } catch (OverflowException actual) { - throw Xunit.Sdk.EqualException.ForMismatchedValues(expected, actual, d1 + " % " + d2); + throw Xunit.Sdk.EqualException.ForMismatchedValues(expected.ToString(), actual.ToString(), d1 + " % " + d2); } } } @@ -1899,7 +1899,7 @@ public static void BigInteger_Floor() unsafe { if (expected.Scale != (byte)(*(uint*)&actual >> BigDecimal.ScaleShift) || expected.CompareTo(new BigDecimal(actual)) != 0) - throw Xunit.Sdk.EqualException.ForMismatchedValues(expected, actual, d1 + " Floor"); + throw Xunit.Sdk.EqualException.ForMismatchedValues(expected.ToString(), actual.ToString(), d1 + " Floor"); } } } @@ -1916,7 +1916,7 @@ public static void BigInteger_Ceiling() unsafe { if (expected.Scale != (byte)(*(uint*)&actual >> BigDecimal.ScaleShift) || expected.CompareTo(new BigDecimal(actual)) != 0) - throw Xunit.Sdk.EqualException.ForMismatchedValues(expected, actual, d1 + " Ceiling"); + throw Xunit.Sdk.EqualException.ForMismatchedValues(expected.ToString(), actual.ToString(), d1 + " Ceiling"); } } } @@ -1933,7 +1933,7 @@ public static void BigInteger_Truncate() unsafe { if (expected.Scale != (byte)(*(uint*)&actual >> BigDecimal.ScaleShift) || expected.CompareTo(new BigDecimal(actual)) != 0) - throw Xunit.Sdk.EqualException.ForMismatchedValues(expected, actual, d1 + " Truncate"); + throw Xunit.Sdk.EqualException.ForMismatchedValues(expected.ToString(), actual.ToString(), d1 + " Truncate"); } } } @@ -1951,7 +1951,7 @@ public static void BigInteger_ToInt32() try { int actual = decimal.ToInt32(d1); - throw Xunit.Sdk.EqualException.ForMismatchedValues(typeof(OverflowException), actual, d1 + " ToInt32"); + throw Xunit.Sdk.EqualException.ForMismatchedValues(typeof(OverflowException).ToString(), actual.ToString(), d1 + " ToInt32"); } catch (OverflowException) { } } @@ -1959,7 +1959,7 @@ public static void BigInteger_ToInt32() { int actual = decimal.ToInt32(d1); if (expected != actual) - throw Xunit.Sdk.EqualException.ForMismatchedValues(expected, actual, d1 + " ToInt32"); + throw Xunit.Sdk.EqualException.ForMismatchedValues(expected.ToString(), actual.ToString(), d1 + " ToInt32"); } } } @@ -1977,7 +1977,7 @@ public static void BigInteger_ToOACurrency() try { long actual = decimal.ToOACurrency(d1); - throw Xunit.Sdk.EqualException.ForMismatchedValues(typeof(OverflowException), actual, d1 + " ToOACurrency"); + throw Xunit.Sdk.EqualException.ForMismatchedValues(typeof(OverflowException).ToString(), actual.ToString(), d1 + " ToOACurrency"); } catch (OverflowException) { } } @@ -1985,7 +1985,7 @@ public static void BigInteger_ToOACurrency() { long actual = decimal.ToOACurrency(d1); if (expected != actual) - throw Xunit.Sdk.EqualException.ForMismatchedValues(expected, actual, d1 + " ToOACurrency"); + throw Xunit.Sdk.EqualException.ForMismatchedValues(expected.ToString(), actual.ToString(), d1 + " ToOACurrency"); } } } @@ -2006,7 +2006,7 @@ public static void BigInteger_Round() unsafe { if (expected.Scale != (byte)(*(uint*)&actual >> BigDecimal.ScaleShift) || expected.CompareTo(new BigDecimal(actual)) != 0) - throw Xunit.Sdk.EqualException.ForMismatchedValues(expected, actual, d1 + " Round(" + j + ")"); + throw Xunit.Sdk.EqualException.ForMismatchedValues(expected.ToString(), actual.ToString(), d1 + " Round(" + j + ")"); } } } @@ -2028,7 +2028,7 @@ public static void BigInteger_RoundAwayFromZero() unsafe { if (expected.Scale != (byte)(*(uint*)&actual >> BigDecimal.ScaleShift) || expected.CompareTo(new BigDecimal(actual)) != 0) - throw Xunit.Sdk.EqualException.ForMismatchedValues(expected, actual, d1 + " RoundAwayFromZero(" + j + ")"); + throw Xunit.Sdk.EqualException.ForMismatchedValues(expected.ToString(), actual.ToString(), d1 + " RoundAwayFromZero(" + j + ")"); } } } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DoubleTests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DoubleTests.GenericMath.cs index d06f69aecced65..f09a6f6e0ad8d0 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DoubleTests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DoubleTests.GenericMath.cs @@ -29,7 +29,7 @@ private static void AssertBitwiseEqual(double expected, double actual) return; } - throw Xunit.Sdk.EqualException.ForMismatchedValues(expected, actual); + throw Xunit.Sdk.EqualException.ForMismatchedValues(expected.ToString(), actual.ToString()); } // diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/HalfTests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/HalfTests.GenericMath.cs index 9c8eaa864a9398..0c46a0c9361a6c 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/HalfTests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/HalfTests.GenericMath.cs @@ -40,7 +40,7 @@ private static void AssertBitwiseEqual(Half expected, Half actual) return; } - throw Xunit.Sdk.EqualException.ForMismatchedValues(expected, actual); + throw Xunit.Sdk.EqualException.ForMismatchedValues(expected.ToString(), actual.ToString()); } // diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Numerics/DimTests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Numerics/DimTests.GenericMath.cs index a42cdd65f8e67f..45c86664f6e597 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Numerics/DimTests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Numerics/DimTests.GenericMath.cs @@ -1014,7 +1014,7 @@ private static void AssertBitwiseEqual(BinaryFloatingPointIeee754Wrapper return; } - throw Xunit.Sdk.EqualException.ForMismatchedValues(expected.Value, actual.Value); + throw Xunit.Sdk.EqualException.ForMismatchedValues(expected.Value.ToString(), actual.Value.ToString()); } } } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/SingleTests.GenericMath.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/SingleTests.GenericMath.cs index 7cfae48cd6f138..a072749010805f 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/SingleTests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/SingleTests.GenericMath.cs @@ -29,7 +29,7 @@ private static void AssertBitwiseEqual(float expected, float actual) return; } - throw Xunit.Sdk.EqualException.ForMismatchedValues(expected, actual); + throw Xunit.Sdk.EqualException.ForMismatchedValues(expected.ToString(), actual.ToString()); } // diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Text/RuneTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Text/RuneTests.cs index f8920bad6091e6..028c8ac5acc5a5 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Text/RuneTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Text/RuneTests.cs @@ -449,8 +449,8 @@ public static void GetUnicodeCategory_AllInputs() { // We'll build up the exception message ourselves so the dev knows what code point failed. throw EqualException.ForMismatchedValues( - expected: UnicodeData.GetUnicodeCategory(rune.Value), - actual: Rune.GetUnicodeCategory(rune), + expected: UnicodeData.GetUnicodeCategory(rune.Value).ToString(), + actual: Rune.GetUnicodeCategory(rune).ToString(), banner: FormattableString.Invariant($@"Rune.GetUnicodeCategory(U+{rune.Value:X4}) returned wrong value.")); } } diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System.Security.Cryptography.Pkcs.csproj b/src/libraries/System.Security.Cryptography.Pkcs/src/System.Security.Cryptography.Pkcs.csproj index 59e67ad0997f38..19cc655555f692 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System.Security.Cryptography.Pkcs.csproj +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System.Security.Cryptography.Pkcs.csproj @@ -25,7 +25,7 @@ System.Security.Cryptography.Pkcs.EnvelopedCms - + diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/EssCertIdV2.xml.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/EssCertIdV2.xml.cs index 68c4fce2aaa806..91c6c3cbc83f46 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/EssCertIdV2.xml.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/EssCertIdV2.xml.cs @@ -8,27 +8,30 @@ namespace System.Security.Cryptography.Pkcs.Asn1 { - [StructLayout(LayoutKind.Sequential)] - internal partial struct EssCertIdV2 + file static class SharedEssCertIdV2 { - private static ReadOnlySpan DefaultHashAlgorithm => [0x30, 0x0B, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01]; - - internal System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn HashAlgorithm; - internal ReadOnlyMemory Hash; - internal System.Security.Cryptography.Pkcs.Asn1.CadesIssuerSerial? IssuerSerial; + internal static ReadOnlySpan DefaultHashAlgorithm => [0x30, 0x0B, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01]; #if DEBUG - static EssCertIdV2() + static SharedEssCertIdV2() { EssCertIdV2 decoded = default; ReadOnlyMemory rebind = default; ValueAsnReader reader; - reader = new ValueAsnReader(DefaultHashAlgorithm, AsnEncodingRules.DER); + reader = new ValueAsnReader(SharedEssCertIdV2.DefaultHashAlgorithm, AsnEncodingRules.DER); System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn.Decode(ref reader, rebind, out decoded.HashAlgorithm); reader.ThrowIfNotEmpty(); } #endif + } + + [StructLayout(LayoutKind.Sequential)] + internal partial struct EssCertIdV2 + { + internal System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn HashAlgorithm; + internal ReadOnlyMemory Hash; + internal System.Security.Cryptography.Pkcs.Asn1.CadesIssuerSerial? IssuerSerial; internal readonly void Encode(AsnWriter writer) { @@ -45,7 +48,7 @@ internal readonly void Encode(AsnWriter writer, Asn1Tag tag) AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER); HashAlgorithm.Encode(tmp); - if (!tmp.EncodedValueEquals(DefaultHashAlgorithm)) + if (!tmp.EncodedValueEquals(SharedEssCertIdV2.DefaultHashAlgorithm)) { tmp.CopyTo(writer); } @@ -115,7 +118,7 @@ private static void DecodeCore(ref ValueAsnReader reader, Asn1Tag expectedTag, R } else { - defaultReader = new ValueAsnReader(DefaultHashAlgorithm, AsnEncodingRules.DER); + defaultReader = new ValueAsnReader(SharedEssCertIdV2.DefaultHashAlgorithm, AsnEncodingRules.DER); System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn.Decode(ref defaultReader, rebind, out decoded.HashAlgorithm); } diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/MessageImprint.xml b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/MessageImprint.xml index e0d6703b9c0782..3f0c647f8b8471 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/MessageImprint.xml +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/MessageImprint.xml @@ -2,7 +2,8 @@ + namespace="System.Security.Cryptography.Pkcs.Asn1" + emitType="both"> - + diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/Rfc3161TstInfo.xml.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/Rfc3161TstInfo.xml.cs index a7f4011fb0bef0..1a71b5f83937ff 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/Rfc3161TstInfo.xml.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Asn1/Rfc3161TstInfo.xml.cs @@ -9,11 +9,26 @@ namespace System.Security.Cryptography.Pkcs.Asn1 { + file static class SharedRfc3161TstInfo + { + internal static ReadOnlySpan DefaultOrdering => [0x01, 0x01, 0x00]; + +#if DEBUG + static SharedRfc3161TstInfo() + { + Rfc3161TstInfo decoded = default; + ValueAsnReader reader; + + reader = new ValueAsnReader(SharedRfc3161TstInfo.DefaultOrdering, AsnEncodingRules.DER); + decoded.Ordering = reader.ReadBoolean(); + reader.ThrowIfNotEmpty(); + } +#endif + } + [StructLayout(LayoutKind.Sequential)] internal partial struct Rfc3161TstInfo { - private static ReadOnlySpan DefaultOrdering => [0x01, 0x01, 0x00]; - internal int Version; internal string Policy; internal System.Security.Cryptography.Pkcs.Asn1.MessageImprint MessageImprint; @@ -25,18 +40,6 @@ internal partial struct Rfc3161TstInfo internal System.Security.Cryptography.Asn1.GeneralNameAsn? Tsa; internal System.Security.Cryptography.Asn1.X509ExtensionAsn[]? Extensions; -#if DEBUG - static Rfc3161TstInfo() - { - Rfc3161TstInfo decoded = default; - ValueAsnReader reader; - - reader = new ValueAsnReader(DefaultOrdering, AsnEncodingRules.DER); - decoded.Ordering = reader.ReadBoolean(); - reader.ThrowIfNotEmpty(); - } -#endif - internal readonly void Encode(AsnWriter writer) { Encode(writer, Asn1Tag.Sequence); @@ -71,7 +74,7 @@ internal readonly void Encode(AsnWriter writer, Asn1Tag tag) AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER, initialCapacity: AsnBoolDerEncodeSize); tmp.WriteBoolean(Ordering); - if (!tmp.EncodedValueEquals(DefaultOrdering)) + if (!tmp.EncodedValueEquals(SharedRfc3161TstInfo.DefaultOrdering)) { tmp.CopyTo(writer); } @@ -183,7 +186,7 @@ private static void DecodeCore(ref ValueAsnReader reader, Asn1Tag expectedTag, R } else { - defaultReader = new ValueAsnReader(DefaultOrdering, AsnEncodingRules.DER); + defaultReader = new ValueAsnReader(SharedRfc3161TstInfo.DefaultOrdering, AsnEncodingRules.DER); decoded.Ordering = defaultReader.ReadBoolean(); } @@ -227,6 +230,125 @@ private static void DecodeCore(ref ValueAsnReader reader, Asn1Tag expectedTag, R } + sequenceReader.ThrowIfNotEmpty(); + } + } + + [StructLayout(LayoutKind.Sequential)] + internal ref partial struct ValueRfc3161TstInfo + { + internal int Version; + internal string Policy; + internal System.Security.Cryptography.Pkcs.Asn1.ValueMessageImprint MessageImprint; + internal ReadOnlySpan SerialNumber; + internal DateTimeOffset GenTime; + internal System.Security.Cryptography.Pkcs.Asn1.Rfc3161Accuracy? Accuracy; + internal bool Ordering; + internal ReadOnlySpan Nonce; + internal bool HasNonce; + internal ReadOnlySpan Tsa; + internal bool HasTsa; + internal ReadOnlySpan Extensions; + internal bool HasExtensions; + + internal static void Decode(ReadOnlySpan encoded, AsnEncodingRules ruleSet, out ValueRfc3161TstInfo decoded) + { + Decode(Asn1Tag.Sequence, encoded, ruleSet, out decoded); + } + + internal static void Decode(Asn1Tag expectedTag, ReadOnlySpan encoded, AsnEncodingRules ruleSet, out ValueRfc3161TstInfo decoded) + { + try + { + ValueAsnReader reader = new ValueAsnReader(encoded, ruleSet); + + DecodeCore(ref reader, expectedTag, out decoded); + reader.ThrowIfNotEmpty(); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + internal static void Decode(scoped ref ValueAsnReader reader, out ValueRfc3161TstInfo decoded) + { + Decode(ref reader, Asn1Tag.Sequence, out decoded); + } + + internal static void Decode(scoped ref ValueAsnReader reader, Asn1Tag expectedTag, out ValueRfc3161TstInfo decoded) + { + try + { + DecodeCore(ref reader, expectedTag, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(scoped ref ValueAsnReader reader, Asn1Tag expectedTag, out ValueRfc3161TstInfo decoded) + { + decoded = default; + ValueAsnReader sequenceReader = reader.ReadSequence(expectedTag); + ValueAsnReader explicitReader; + ValueAsnReader defaultReader; + + + if (!sequenceReader.TryReadInt32(out decoded.Version)) + { + sequenceReader.ThrowIfNotEmpty(); + } + + decoded.Policy = sequenceReader.ReadObjectIdentifier(); + System.Security.Cryptography.Pkcs.Asn1.ValueMessageImprint.Decode(ref sequenceReader, out decoded.MessageImprint); + decoded.SerialNumber = sequenceReader.ReadIntegerBytes(); + decoded.GenTime = sequenceReader.ReadGeneralizedTime(); + + if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(Asn1Tag.Sequence)) + { + System.Security.Cryptography.Pkcs.Asn1.Rfc3161Accuracy tmpAccuracy; + System.Security.Cryptography.Pkcs.Asn1.Rfc3161Accuracy.Decode(ref sequenceReader, out tmpAccuracy); + decoded.Accuracy = tmpAccuracy; + + } + + + if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(Asn1Tag.Boolean)) + { + decoded.Ordering = sequenceReader.ReadBoolean(); + } + else + { + defaultReader = new ValueAsnReader(SharedRfc3161TstInfo.DefaultOrdering, AsnEncodingRules.DER); + decoded.Ordering = defaultReader.ReadBoolean(); + } + + + if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(Asn1Tag.Integer)) + { + decoded.Nonce = sequenceReader.ReadIntegerBytes(); + decoded.HasNonce = true; + } + + + if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 0))) + { + explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); + decoded.Tsa = explicitReader.ReadEncodedValue(); + decoded.HasTsa = true; + explicitReader.ThrowIfNotEmpty(); + } + + + if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1))) + { + decoded.Extensions = sequenceReader.ReadEncodedValue(); + decoded.HasExtensions = true; + } + + sequenceReader.ThrowIfNotEmpty(); } } diff --git a/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj b/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj index 2a7ddd85d7f045..c0df094935c129 100644 --- a/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj +++ b/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj @@ -22,7 +22,7 @@ - + DefaultCA => [0x01, 0x01, 0x00]; - - internal bool CA; - internal int? PathLengthConstraint; + internal static ReadOnlySpan DefaultCA => [0x01, 0x01, 0x00]; #if DEBUG - static BasicConstraintsAsn() + static SharedBasicConstraintsAsn() { BasicConstraintsAsn decoded = default; ValueAsnReader reader; - reader = new ValueAsnReader(DefaultCA, AsnEncodingRules.DER); + reader = new ValueAsnReader(SharedBasicConstraintsAsn.DefaultCA, AsnEncodingRules.DER); decoded.CA = reader.ReadBoolean(); reader.ThrowIfNotEmpty(); } #endif + } + + [StructLayout(LayoutKind.Sequential)] + internal partial struct BasicConstraintsAsn + { + internal bool CA; + internal int? PathLengthConstraint; internal readonly void Encode(AsnWriter writer) { @@ -44,7 +47,7 @@ internal readonly void Encode(AsnWriter writer, Asn1Tag tag) AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER, initialCapacity: AsnBoolDerEncodeSize); tmp.WriteBoolean(CA); - if (!tmp.EncodedValueEquals(DefaultCA)) + if (!tmp.EncodedValueEquals(SharedBasicConstraintsAsn.DefaultCA)) { tmp.CopyTo(writer); } @@ -110,7 +113,7 @@ private static void DecodeCore(ref ValueAsnReader reader, Asn1Tag expectedTag, o } else { - defaultReader = new ValueAsnReader(DefaultCA, AsnEncodingRules.DER); + defaultReader = new ValueAsnReader(SharedBasicConstraintsAsn.DefaultCA, AsnEncodingRules.DER); decoded.CA = defaultReader.ReadBoolean(); } diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/Asn1/CertificationRequestInfoAsn.xml b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/Asn1/CertificationRequestInfoAsn.xml index 41cd3579d1ccf5..d6b9130e1dbf9c 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/Asn1/CertificationRequestInfoAsn.xml +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/Asn1/CertificationRequestInfoAsn.xml @@ -2,7 +2,8 @@ + namespace="System.Security.Cryptography.X509Certificates.Asn1" + emitType="both"> - - - + + + - \ No newline at end of file + diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/Asn1/CertificationRequestInfoAsn.xml.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/Asn1/CertificationRequestInfoAsn.xml.cs index 403949afeac484..12ac6712256a99 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/Asn1/CertificationRequestInfoAsn.xml.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/Asn1/CertificationRequestInfoAsn.xml.cs @@ -132,4 +132,125 @@ private static void DecodeCore(ref ValueAsnReader reader, Asn1Tag expectedTag, R sequenceReader.ThrowIfNotEmpty(); } } + + [StructLayout(LayoutKind.Sequential)] + internal ref partial struct ValueCertificationRequestInfoAsn + { + internal System.Numerics.BigInteger Version; + internal ReadOnlySpan Subject; + internal System.Security.Cryptography.Asn1.ValueSubjectPublicKeyInfoAsn SubjectPublicKeyInfo; + internal ReadOnlySpan Attributes; + + internal static void Decode(ReadOnlySpan encoded, AsnEncodingRules ruleSet, out ValueCertificationRequestInfoAsn decoded) + { + Decode(Asn1Tag.Sequence, encoded, ruleSet, out decoded); + } + + internal static void Decode(Asn1Tag expectedTag, ReadOnlySpan encoded, AsnEncodingRules ruleSet, out ValueCertificationRequestInfoAsn decoded) + { + try + { + ValueAsnReader reader = new ValueAsnReader(encoded, ruleSet); + + DecodeCore(ref reader, expectedTag, out decoded); + reader.ThrowIfNotEmpty(); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + internal static void Decode(scoped ref ValueAsnReader reader, out ValueCertificationRequestInfoAsn decoded) + { + Decode(ref reader, Asn1Tag.Sequence, out decoded); + } + + internal static void Decode(scoped ref ValueAsnReader reader, Asn1Tag expectedTag, out ValueCertificationRequestInfoAsn decoded) + { + try + { + DecodeCore(ref reader, expectedTag, out decoded); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); + } + } + + private static void DecodeCore(scoped ref ValueAsnReader reader, Asn1Tag expectedTag, out ValueCertificationRequestInfoAsn decoded) + { + decoded = default; + ValueAsnReader sequenceReader = reader.ReadSequence(expectedTag); + + decoded.Version = sequenceReader.ReadInteger(); + if (!sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag((UniversalTagNumber)16))) + { + throw new CryptographicException(); + } + + decoded.Subject = sequenceReader.ReadEncodedValue(); + System.Security.Cryptography.Asn1.ValueSubjectPublicKeyInfoAsn.Decode(ref sequenceReader, out decoded.SubjectPublicKeyInfo); + + if (!sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 0))) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } + + decoded.Attributes = sequenceReader.ReadEncodedValue(); + + sequenceReader.ThrowIfNotEmpty(); + } + + + internal AttributesEnumerable GetAttributes(AsnEncodingRules ruleSet) + { + return new AttributesEnumerable(Attributes, ruleSet); + } + + internal readonly ref struct AttributesEnumerable + { + private readonly ReadOnlySpan _encoded; + private readonly AsnEncodingRules _ruleSet; + + internal AttributesEnumerable(ReadOnlySpan encoded, AsnEncodingRules ruleSet) + { + _encoded = encoded; + _ruleSet = ruleSet; + } + + public Enumerator GetEnumerator() => new Enumerator(_encoded, _ruleSet); + + internal ref struct Enumerator + { + private ValueAsnReader _reader; + private System.Security.Cryptography.Asn1.ValueAttributeAsn _current; + + internal Enumerator(ReadOnlySpan encoded, AsnEncodingRules ruleSet) + { + if (!encoded.IsEmpty) + { + ValueAsnReader outerReader = new ValueAsnReader(encoded, ruleSet); + _reader = outerReader.ReadSetOf(new Asn1Tag(TagClass.ContextSpecific, 0)); + outerReader.ThrowIfNotEmpty(); + } + + _current = default; + } + + public System.Security.Cryptography.Asn1.ValueAttributeAsn Current => _current; + + public bool MoveNext() + { + if (!_reader.HasData) + { + return false; + } + + System.Security.Cryptography.Asn1.ValueAttributeAsn.Decode(ref _reader, out _current); + return true; + } + } + } + } } diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/Asn1/TbsCertificateAsn.xml.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/Asn1/TbsCertificateAsn.xml.cs index 0f0b267b23639b..4242feccf97118 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/Asn1/TbsCertificateAsn.xml.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/Asn1/TbsCertificateAsn.xml.cs @@ -9,29 +9,17 @@ namespace System.Security.Cryptography.X509Certificates.Asn1 { - [StructLayout(LayoutKind.Sequential)] - internal partial struct TbsCertificateAsn + file static class SharedTbsCertificateAsn { - private static ReadOnlySpan DefaultVersion => [0x02, 0x01, 0x00]; - - internal int Version; - internal ReadOnlyMemory SerialNumber; - internal System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn SignatureAlgorithm; - internal ReadOnlyMemory Issuer; - internal System.Security.Cryptography.X509Certificates.Asn1.ValidityAsn Validity; - internal ReadOnlyMemory Subject; - internal System.Security.Cryptography.Asn1.SubjectPublicKeyInfoAsn SubjectPublicKeyInfo; - internal ReadOnlyMemory? IssuerUniqueId; - internal ReadOnlyMemory? SubjectUniqueId; - internal System.Security.Cryptography.Asn1.X509ExtensionAsn[]? Extensions; + internal static ReadOnlySpan DefaultVersion => [0x02, 0x01, 0x00]; #if DEBUG - static TbsCertificateAsn() + static SharedTbsCertificateAsn() { TbsCertificateAsn decoded = default; ValueAsnReader reader; - reader = new ValueAsnReader(DefaultVersion, AsnEncodingRules.DER); + reader = new ValueAsnReader(SharedTbsCertificateAsn.DefaultVersion, AsnEncodingRules.DER); if (!reader.TryReadInt32(out decoded.Version)) { @@ -41,6 +29,21 @@ static TbsCertificateAsn() reader.ThrowIfNotEmpty(); } #endif + } + + [StructLayout(LayoutKind.Sequential)] + internal partial struct TbsCertificateAsn + { + internal int Version; + internal ReadOnlyMemory SerialNumber; + internal System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn SignatureAlgorithm; + internal ReadOnlyMemory Issuer; + internal System.Security.Cryptography.X509Certificates.Asn1.ValidityAsn Validity; + internal ReadOnlyMemory Subject; + internal System.Security.Cryptography.Asn1.SubjectPublicKeyInfoAsn SubjectPublicKeyInfo; + internal ReadOnlyMemory? IssuerUniqueId; + internal ReadOnlyMemory? SubjectUniqueId; + internal System.Security.Cryptography.Asn1.X509ExtensionAsn[]? Extensions; internal readonly void Encode(AsnWriter writer) { @@ -58,7 +61,7 @@ internal readonly void Encode(AsnWriter writer, Asn1Tag tag) AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER, initialCapacity: AsnManagedIntegerDerMaxEncodeSize); tmp.WriteInteger(Version); - if (!tmp.EncodedValueEquals(DefaultVersion)) + if (!tmp.EncodedValueEquals(SharedTbsCertificateAsn.DefaultVersion)) { writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); tmp.CopyTo(writer); @@ -197,7 +200,7 @@ private static void DecodeCore(ref ValueAsnReader reader, Asn1Tag expectedTag, R } else { - defaultReader = new ValueAsnReader(DefaultVersion, AsnEncodingRules.DER); + defaultReader = new ValueAsnReader(SharedTbsCertificateAsn.DefaultVersion, AsnEncodingRules.DER); if (!defaultReader.TryReadInt32(out decoded.Version)) { diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/CertificateRequest.Load.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/CertificateRequest.Load.cs index 57a102169ad5c4..9644f1bc90e86c 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/CertificateRequest.Load.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/CertificateRequest.Load.cs @@ -149,135 +149,141 @@ private static unsafe CertificateRequest LoadSigningRequest( outer.ThrowIfNotEmpty(); } - fixed (byte* p10ptr = pkcs10) + ReadOnlySpan encodedRequestInfo = pkcs10Asn.PeekEncodedValue(); + ValueCertificationRequestInfoAsn requestInfo; + ValueAlgorithmIdentifierAsn algorithmIdentifier; + ReadOnlySpan signature; + int signatureUnusedBitCount; + + ValueCertificationRequestInfoAsn.Decode(ref pkcs10Asn, out requestInfo); + ValueAlgorithmIdentifierAsn.Decode(ref pkcs10Asn, out algorithmIdentifier); + + if (!pkcs10Asn.TryReadPrimitiveBitString(out signatureUnusedBitCount, out signature)) { - using (PointerMemoryManager manager = new PointerMemoryManager(p10ptr, encodedLength)) - { - ReadOnlyMemory rebind = manager.Memory; - ReadOnlySpan encodedRequestInfo = pkcs10Asn.PeekEncodedValue(); - CertificationRequestInfoAsn requestInfo; - AlgorithmIdentifierAsn algorithmIdentifier; - ReadOnlySpan signature; - int signatureUnusedBitCount; + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } - CertificationRequestInfoAsn.Decode(ref pkcs10Asn, rebind, out requestInfo); - AlgorithmIdentifierAsn.Decode(ref pkcs10Asn, rebind, out algorithmIdentifier); + pkcs10Asn.ThrowIfNotEmpty(); - if (!pkcs10Asn.TryReadPrimitiveBitString(out signatureUnusedBitCount, out signature)) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } + if (requestInfo.Version < 0) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); + } - pkcs10Asn.ThrowIfNotEmpty(); + // They haven't bumped from v0 to v1 as of 2022. + const int MaxSupportedVersion = 0; - if (requestInfo.Version < 0) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } + if (requestInfo.Version != MaxSupportedVersion) + { + throw new CryptographicException( + SR.Format( + SR.Cryptography_CertReq_Load_VersionTooNew, + requestInfo.Version, + MaxSupportedVersion)); + } + + PublicKey publicKey = PublicKey.DecodeSubjectPublicKeyInfo(ref requestInfo.SubjectPublicKeyInfo); + + if (!skipSignatureValidation) + { + // None of the supported signature algorithms support signatures that are not full bytes. + // So, shortcut the verification on the bit length + if (signatureUnusedBitCount != 0 || + !VerifyX509Signature(encodedRequestInfo, signature, publicKey, ref algorithmIdentifier)) + { + throw new CryptographicException(SR.Cryptography_CertReq_SignatureVerificationFailed); + } + } + + X500DistinguishedName subject = new X500DistinguishedName(requestInfo.Subject); - // They haven't bumped from v0 to v1 as of 2022. - const int MaxSupportedVersion = 0; + req = new CertificateRequest( + subject, + publicKey, + signerHashAlgorithm, + signerSignaturePadding); - if (requestInfo.Version != MaxSupportedVersion) + bool foundCertExt = false; + + foreach (ValueAttributeAsn attr in requestInfo.GetAttributes(AsnEncodingRules.DER)) + { + if (attr.AttrType == Oids.Pkcs9ExtensionRequest) + { + if (foundCertExt) { throw new CryptographicException( - SR.Format( - SR.Cryptography_CertReq_Load_VersionTooNew, - requestInfo.Version, - MaxSupportedVersion)); + SR.Cryptography_CertReq_Load_DuplicateExtensionRequests); } - PublicKey publicKey = PublicKey.DecodeSubjectPublicKeyInfo(ref requestInfo.SubjectPublicKeyInfo); + foundCertExt = true; - if (!skipSignatureValidation) + scoped ReadOnlySpan firstAttrValue = default; + bool foundAttrValue = false; + + foreach (ReadOnlySpan values in attr.GetAttrValues(AsnEncodingRules.DER)) { - // None of the supported signature algorithms support signatures that are not full bytes. - // So, shortcut the verification on the bit length - if (signatureUnusedBitCount != 0 || - !VerifyX509Signature(encodedRequestInfo, signature, publicKey, algorithmIdentifier)) + if (foundAttrValue) { - throw new CryptographicException(SR.Cryptography_CertReq_SignatureVerificationFailed); + throw new CryptographicException( + SR.Cryptography_CertReq_Load_DuplicateExtensionRequests); } + + firstAttrValue = values; + foundAttrValue = true; } - X500DistinguishedName subject = new X500DistinguishedName(requestInfo.Subject.Span); + if (!foundAttrValue) + { + throw new CryptographicException(SR.Cryptography_CertReq_Load_DuplicateExtensionRequests); + } - req = new CertificateRequest( - subject, - publicKey, - signerHashAlgorithm, - signerSignaturePadding); + ValueAsnReader extsReader = new ValueAsnReader( + firstAttrValue, + AsnEncodingRules.DER); + + ValueAsnReader exts = extsReader.ReadSequence(); + extsReader.ThrowIfNotEmpty(); - if (requestInfo.Attributes is not null) + // Minimum length is 1, so do..while + do { - bool foundCertExt = false; + ValueX509ExtensionAsn.Decode(ref exts, out ValueX509ExtensionAsn extAsn); - foreach (AttributeAsn attr in requestInfo.Attributes) + if (unsafeLoadCertificateExtensions) { - if (attr.AttrType == Oids.Pkcs9ExtensionRequest) + X509Extension ext = new X509Extension( + extAsn.ExtnId, + extAsn.ExtnValue, + extAsn.Critical); + + X509Extension? rich = + X509Certificate2.CreateCustomExtensionIfAny(extAsn.ExtnId); + + if (rich is not null) { - if (foundCertExt) - { - throw new CryptographicException( - SR.Cryptography_CertReq_Load_DuplicateExtensionRequests); - } - - foundCertExt = true; - - if (attr.AttrValues.Length != 1) - { - throw new CryptographicException( - SR.Cryptography_CertReq_Load_DuplicateExtensionRequests); - } - - ValueAsnReader extsReader = new ValueAsnReader( - attr.AttrValues[0].Span, - AsnEncodingRules.DER); - - ValueAsnReader exts = extsReader.ReadSequence(); - extsReader.ThrowIfNotEmpty(); - - // Minimum length is 1, so do..while - do - { - X509ExtensionAsn.Decode(ref exts, rebind, out X509ExtensionAsn extAsn); - - if (unsafeLoadCertificateExtensions) - { - X509Extension ext = new X509Extension( - extAsn.ExtnId, - extAsn.ExtnValue.Span, - extAsn.Critical); - - X509Extension? rich = - X509Certificate2.CreateCustomExtensionIfAny(extAsn.ExtnId); - - if (rich is not null) - { - rich.CopyFrom(ext); - req.CertificateExtensions.Add(rich); - } - else - { - req.CertificateExtensions.Add(ext); - } - } - } while (exts.HasData); + rich.CopyFrom(ext); + req.CertificateExtensions.Add(rich); } else { - if (attr.AttrValues.Length == 0) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); - } - - foreach (ReadOnlyMemory val in attr.AttrValues) - { - req.OtherRequestAttributes.Add( - new AsnEncodedData(attr.AttrType, val.Span)); - } + req.CertificateExtensions.Add(ext); } } + } while (exts.HasData); + } + else + { + bool anyAttrValues = false; + + foreach (ReadOnlySpan val in attr.GetAttrValues(AsnEncodingRules.DER)) + { + req.OtherRequestAttributes.Add(new AsnEncodedData(attr.AttrType, val)); + anyAttrValues = true; + } + + if (!anyAttrValues) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } } } @@ -295,7 +301,7 @@ private static bool VerifyX509Signature( ReadOnlySpan toBeSigned, ReadOnlySpan signature, PublicKey publicKey, - AlgorithmIdentifierAsn algorithmIdentifier) + ref readonly ValueAlgorithmIdentifierAsn algorithmIdentifier) { RSA? rsa = publicKey.GetRSAPublicKey(); ECDsa? ecdsa = publicKey.GetECDsaPublicKey(); @@ -308,14 +314,15 @@ private static bool VerifyX509Signature( if (algorithmIdentifier.Algorithm == Oids.RsaPss) { - if (rsa is null || !algorithmIdentifier.Parameters.HasValue) + if (rsa is null || !algorithmIdentifier.HasParameters) { return false; } - PssParamsAsn pssParams = PssParamsAsn.Decode( - algorithmIdentifier.Parameters.GetValueOrDefault(), - AsnEncodingRules.DER); + ValuePssParamsAsn.Decode( + algorithmIdentifier.Parameters, + AsnEncodingRules.DER, + out ValuePssParamsAsn pssParams); RSASignaturePadding padding = pssParams.GetSignaturePadding(); hashAlg = HashAlgorithmName.FromOid(pssParams.HashAlgorithm.Algorithm); diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/PublicKey.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/PublicKey.cs index 2c3d2d8875cf59..e4c850bcb5b3ea 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/PublicKey.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/PublicKey.cs @@ -451,36 +451,32 @@ internal AsnWriter EncodeSubjectPublicKeyInfo() return writer; } - private static unsafe int DecodeSubjectPublicKeyInfo( + private static int DecodeSubjectPublicKeyInfo( ReadOnlySpan source, out Oid oid, out AsnEncodedData? parameters, out AsnEncodedData keyValue) { - fixed (byte* ptr = &MemoryMarshal.GetReference(source)) - using (MemoryManager manager = new PointerMemoryManager(ptr, source.Length)) - { - ValueAsnReader reader = new ValueAsnReader(source, AsnEncodingRules.DER); + ValueAsnReader reader = new ValueAsnReader(source, AsnEncodingRules.DER); - int read; - SubjectPublicKeyInfoAsn spki; + int read; + ValueSubjectPublicKeyInfoAsn spki; - try - { - read = reader.PeekEncodedValue().Length; - SubjectPublicKeyInfoAsn.Decode(ref reader, manager.Memory, out spki); - } - catch (AsnContentException e) - { - throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); - } - - DecodeSubjectPublicKeyInfo(ref spki, out oid, out parameters, out keyValue); - return read; + try + { + read = reader.PeekEncodedValue().Length; + ValueSubjectPublicKeyInfoAsn.Decode(ref reader, out spki); + } + catch (AsnContentException e) + { + throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); } + + DecodeSubjectPublicKeyInfo(ref spki, out oid, out parameters, out keyValue); + return read; } - internal static PublicKey DecodeSubjectPublicKeyInfo(ref SubjectPublicKeyInfoAsn spki) + internal static PublicKey DecodeSubjectPublicKeyInfo(ref ValueSubjectPublicKeyInfoAsn spki) { DecodeSubjectPublicKeyInfo( ref spki, @@ -492,18 +488,16 @@ internal static PublicKey DecodeSubjectPublicKeyInfo(ref SubjectPublicKeyInfoAsn } private static void DecodeSubjectPublicKeyInfo( - ref SubjectPublicKeyInfoAsn spki, + ref ValueSubjectPublicKeyInfoAsn spki, out Oid oid, out AsnEncodedData? parameters, out AsnEncodedData keyValue) { oid = new Oid(spki.Algorithm.Algorithm, null); - keyValue = new AsnEncodedData(spki.SubjectPublicKey.Span); - parameters = spki.Algorithm.Parameters switch - { - ReadOnlyMemory algParameters => new AsnEncodedData(algParameters.Span), - _ => null, - }; + keyValue = new AsnEncodedData(spki.SubjectPublicKey); + parameters = spki.Algorithm.HasParameters ? + new AsnEncodedData(spki.Algorithm.Parameters) : + null; } } } diff --git a/src/libraries/System.Security.Cryptography/tests/System.Security.Cryptography.Tests.csproj b/src/libraries/System.Security.Cryptography/tests/System.Security.Cryptography.Tests.csproj index 12bdec3a60f84f..dd8864117bc665 100644 --- a/src/libraries/System.Security.Cryptography/tests/System.Security.Cryptography.Tests.csproj +++ b/src/libraries/System.Security.Cryptography/tests/System.Security.Cryptography.Tests.csproj @@ -20,7 +20,7 @@ true - + Common\System\Security\Cryptography\Asn1\AlgorithmIdentifierAsn.xml diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs index 329a369b4a0135..a865466d5f40b8 100644 --- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs +++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs @@ -47,7 +47,7 @@ private sealed class Parser private readonly Dictionary _generatedTypes = new(SymbolEqualityComparer.Default); #pragma warning restore - public List Diagnostics { get; } = new(); + public List Diagnostics { get; } = new(); private Location? _contextClassLocation; public void ReportDiagnostic(DiagnosticDescriptor descriptor, Location? location, params object?[]? messageArgs) @@ -60,7 +60,7 @@ public void ReportDiagnostic(DiagnosticDescriptor descriptor, Location? location location = _contextClassLocation; } - Diagnostics.Add(DiagnosticInfo.Create(descriptor, location, messageArgs)); + Diagnostics.Add(Diagnostic.Create(descriptor, location, messageArgs)); } public Parser(KnownTypeSymbols knownSymbols) diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Roslyn3.11.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Roslyn3.11.cs index 965bf7fc210751..5397715ede9da8 100644 --- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Roslyn3.11.cs +++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Roslyn3.11.cs @@ -75,9 +75,9 @@ public void Execute(GeneratorExecutionContext executionContext) } // Stage 2. Report any diagnostics gathered by the parser. - foreach (DiagnosticInfo diagnosticInfo in parser.Diagnostics) + foreach (Diagnostic diagnostic in parser.Diagnostics) { - executionContext.ReportDiagnostic(diagnosticInfo.CreateDiagnostic()); + executionContext.ReportDiagnostic(diagnostic); } if (contextGenerationSpecs is null) diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Roslyn4.0.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Roslyn4.0.cs index 75f1f51269e7a6..79fab10a1cfc34 100644 --- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Roslyn4.0.cs +++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Roslyn4.0.cs @@ -10,7 +10,6 @@ #if !ROSLYN4_4_OR_GREATER using Microsoft.CodeAnalysis.DotnetRuntime.Extensions; #endif -using SourceGenerators; namespace System.Text.Json.SourceGeneration { @@ -32,7 +31,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) IncrementalValueProvider knownTypeSymbols = context.CompilationProvider .Select((compilation, _) => new KnownTypeSymbols(compilation)); - IncrementalValuesProvider<(ContextGenerationSpec?, ImmutableEquatableArray)> contextGenerationSpecs = context.SyntaxProvider + IncrementalValuesProvider<(ContextGenerationSpec?, ImmutableArray)> contextGenerationSpecs = context.SyntaxProvider .ForAttributeWithMetadataName( #if !ROSLYN4_4_OR_GREATER context, @@ -54,7 +53,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) #pragma warning restore RS1035 Parser parser = new(tuple.Right); ContextGenerationSpec? contextGenerationSpec = parser.ParseContextGenerationSpec(tuple.Left.ContextClass, tuple.Left.SemanticModel, cancellationToken); - ImmutableEquatableArray diagnostics = parser.Diagnostics.ToImmutableEquatableArray(); + ImmutableArray diagnostics = parser.Diagnostics.ToImmutableArray(); return (contextGenerationSpec, diagnostics); #pragma warning disable RS1035 } @@ -69,23 +68,36 @@ public void Initialize(IncrementalGeneratorInitializationContext context) #endif ; - context.RegisterSourceOutput(contextGenerationSpecs, ReportDiagnosticsAndEmitSource); + // Project the combined pipeline result to just the equatable model, discarding diagnostics. + // ContextGenerationSpec implements value equality, so Roslyn's Select operator will compare + // successive model snapshots and only propagate changes downstream when the model structurally + // differs. This ensures source generation is fully incremental: re-emitting code only when + // the serialization spec actually changes, not on every keystroke or positional shift. + IncrementalValuesProvider sourceGenerationSpecs = + contextGenerationSpecs.Select(static (t, _) => t.Item1); + + context.RegisterSourceOutput(sourceGenerationSpecs, EmitSource); + + // Project to just the diagnostics, discarding the model. ImmutableArray does not + // implement value equality, so Roslyn's incremental pipeline uses reference equality for these + // values — the callback fires on every compilation change. This is by design: diagnostic + // emission is cheap, and we need fresh SourceLocation instances that are pragma-suppressible + // (cf. https://github.com/dotnet/runtime/issues/92509). + // No source code is generated from this pipeline — it exists solely to report diagnostics. + IncrementalValuesProvider> diagnostics = + contextGenerationSpecs.Select(static (t, _) => t.Item2); + + context.RegisterSourceOutput(diagnostics, EmitDiagnostics); } - private void ReportDiagnosticsAndEmitSource(SourceProductionContext sourceProductionContext, (ContextGenerationSpec? ContextGenerationSpec, ImmutableEquatableArray Diagnostics) input) + private void EmitSource(SourceProductionContext sourceProductionContext, ContextGenerationSpec? contextGenerationSpec) { - // Report any diagnostics ahead of emitting. - foreach (DiagnosticInfo diagnostic in input.Diagnostics) - { - sourceProductionContext.ReportDiagnostic(diagnostic.CreateDiagnostic()); - } - - if (input.ContextGenerationSpec is null) + if (contextGenerationSpec is null) { return; } - OnSourceEmitting?.Invoke(ImmutableArray.Create(input.ContextGenerationSpec)); + OnSourceEmitting?.Invoke(ImmutableArray.Create(contextGenerationSpec)); // Ensure the source generator emits number literals using invariant culture. // This prevents issues such as locale-specific negative signs (e.g., U+2212 in fi-FI) @@ -96,7 +108,7 @@ private void ReportDiagnosticsAndEmitSource(SourceProductionContext sourceProduc try { Emitter emitter = new(sourceProductionContext); - emitter.Emit(input.ContextGenerationSpec); + emitter.Emit(contextGenerationSpec); } finally { @@ -105,6 +117,14 @@ private void ReportDiagnosticsAndEmitSource(SourceProductionContext sourceProduc #pragma warning restore RS1035 } + private static void EmitDiagnostics(SourceProductionContext context, ImmutableArray diagnostics) + { + foreach (Diagnostic diagnostic in diagnostics) + { + context.ReportDiagnostic(diagnostic); + } + } + /// /// Instrumentation helper for unit tests. /// diff --git a/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.targets b/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.targets index aa7ae500ce7870..2d4351a3eab530 100644 --- a/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.targets +++ b/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.targets @@ -31,7 +31,6 @@ - diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/JsonSourceGeneratorDiagnosticsTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/JsonSourceGeneratorDiagnosticsTests.cs index b8e3073a857cf5..5712a52072a110 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/JsonSourceGeneratorDiagnosticsTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/JsonSourceGeneratorDiagnosticsTests.cs @@ -5,6 +5,7 @@ using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; using Xunit; namespace System.Text.Json.SourceGeneration.UnitTests @@ -755,5 +756,33 @@ public partial class MyContext : JsonSerializerContext CompilationHelper.AssertEqualDiagnosticMessages(expectedDiagnostics, result.Diagnostics); } #endif + + [Fact] + public void Diagnostic_HasPragmaSuppressibleLocation() + { + // SYSLIB1038: JsonInclude attribute on inaccessible member (Warning, configurable). + string source = """ + #pragma warning disable SYSLIB1038 + using System.Text.Json.Serialization; + + namespace Test + { + public class MyClass + { + [JsonInclude] + private int PrivateField; + } + + [JsonSerializable(typeof(MyClass))] + public partial class JsonContext : JsonSerializerContext { } + } + """; + + Compilation compilation = CompilationHelper.CreateCompilation(source); + JsonSourceGeneratorResult result = CompilationHelper.RunJsonSourceGenerator(compilation, disableDiagnosticValidation: true); + var effective = CompilationWithAnalyzers.GetEffectiveDiagnostics(result.Diagnostics, compilation); + Diagnostic diagnostic = Assert.Single(effective, d => d.Id == "SYSLIB1038"); + Assert.True(diagnostic.IsSuppressed); + } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/JsonSourceGeneratorIncrementalTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/JsonSourceGeneratorIncrementalTests.cs index d08c07c2e741ea..7be4413b44befa 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/JsonSourceGeneratorIncrementalTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/JsonSourceGeneratorIncrementalTests.cs @@ -145,7 +145,6 @@ public static void SourceGenModelDoesNotEncapsulateSymbolsOrCompilationData(Func { JsonSourceGeneratorResult result = CompilationHelper.RunJsonSourceGenerator(factory(), disableDiagnosticValidation: true); WalkObjectGraph(result.ContextGenerationSpecs); - WalkObjectGraph(result.Diagnostics); static void WalkObjectGraph(object obj) { diff --git a/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Parser.cs b/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Parser.cs index 947d0bc0b049b5..1d1df92c107d9b 100644 --- a/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Parser.cs +++ b/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Parser.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Immutable; @@ -20,7 +20,7 @@ public partial class RegexGenerator private const string GeneratedRegexAttributeName = "System.Text.RegularExpressions.GeneratedRegexAttribute"; /// - /// Returns null if nothing to do, if there's an error to report, + /// Returns null if nothing to do, a if there's an error to report, /// or if the type was analyzed successfully. /// private static object? GetRegexMethodDataOrFailureDiagnostic( @@ -32,7 +32,7 @@ public partial class RegexGenerator // of being able to flag invalid use when [GeneratedRegex] is applied incorrectly. // Otherwise, if the ForAttributeWithMetadataName call excluded these, [GeneratedRegex] // could be applied to them and we wouldn't be able to issue a diagnostic. - return new DiagnosticData(DiagnosticDescriptors.RegexMemberMustHaveValidSignature, GetComparableLocation(context.TargetNode)); + return Diagnostic.Create(DiagnosticDescriptors.RegexMemberMustHaveValidSignature, context.TargetNode.GetLocation()); } var memberSyntax = (MemberDeclarationSyntax)context.TargetNode; @@ -62,19 +62,19 @@ public partial class RegexGenerator ImmutableArray boundAttributes = context.Attributes; if (boundAttributes.Length != 1) { - return new DiagnosticData(DiagnosticDescriptors.MultipleGeneratedRegexAttributes, GetComparableLocation(memberSyntax)); + return Diagnostic.Create(DiagnosticDescriptors.MultipleGeneratedRegexAttributes, memberSyntax.GetLocation()); } AttributeData generatedRegexAttr = boundAttributes[0]; if (generatedRegexAttr.ConstructorArguments.Any(ca => ca.Kind == TypedConstantKind.Error)) { - return new DiagnosticData(DiagnosticDescriptors.InvalidGeneratedRegexAttribute, GetComparableLocation(memberSyntax)); + return Diagnostic.Create(DiagnosticDescriptors.InvalidGeneratedRegexAttribute, memberSyntax.GetLocation()); } ImmutableArray items = generatedRegexAttr.ConstructorArguments; if (items.Length is 0 or > 4) { - return new DiagnosticData(DiagnosticDescriptors.InvalidGeneratedRegexAttribute, GetComparableLocation(memberSyntax)); + return Diagnostic.Create(DiagnosticDescriptors.InvalidGeneratedRegexAttribute, memberSyntax.GetLocation()); } string? pattern = items[0].Value as string; @@ -106,7 +106,7 @@ public partial class RegexGenerator if (pattern is null || cultureName is null) { - return new DiagnosticData(DiagnosticDescriptors.InvalidRegexArguments, GetComparableLocation(memberSyntax), "(null)"); + return Diagnostic.Create(DiagnosticDescriptors.InvalidRegexArguments, memberSyntax.GetLocation(), "(null)"); } bool nullableRegex; @@ -118,7 +118,7 @@ public partial class RegexGenerator regexMethodSymbol.Arity != 0 || !SymbolEqualityComparer.Default.Equals(regexMethodSymbol.ReturnType, regexSymbol)) { - return new DiagnosticData(DiagnosticDescriptors.RegexMemberMustHaveValidSignature, GetComparableLocation(memberSyntax)); + return Diagnostic.Create(DiagnosticDescriptors.RegexMemberMustHaveValidSignature, memberSyntax.GetLocation()); } nullableRegex = regexMethodSymbol.ReturnNullableAnnotation == NullableAnnotation.Annotated; @@ -132,7 +132,7 @@ public partial class RegexGenerator regexPropertySymbol.SetMethod is not null || !SymbolEqualityComparer.Default.Equals(regexPropertySymbol.Type, regexSymbol)) { - return new DiagnosticData(DiagnosticDescriptors.RegexMemberMustHaveValidSignature, GetComparableLocation(memberSyntax)); + return Diagnostic.Create(DiagnosticDescriptors.RegexMemberMustHaveValidSignature, memberSyntax.GetLocation()); } nullableRegex = regexPropertySymbol.NullableAnnotation == NullableAnnotation.Annotated; @@ -154,7 +154,7 @@ regexPropertySymbol.SetMethod is not null || } catch (Exception e) { - return new DiagnosticData(DiagnosticDescriptors.InvalidRegexArguments, GetComparableLocation(memberSyntax), e.Message); + return Diagnostic.Create(DiagnosticDescriptors.InvalidRegexArguments, memberSyntax.GetLocation(), e.Message); } if ((regexOptionsWithPatternOptions & RegexOptions.IgnoreCase) != 0 && !string.IsNullOrEmpty(cultureName)) @@ -162,7 +162,7 @@ regexPropertySymbol.SetMethod is not null || if ((regexOptions & RegexOptions.CultureInvariant) != 0) { // User passed in both a culture name and set RegexOptions.CultureInvariant which causes an explicit conflict. - return new DiagnosticData(DiagnosticDescriptors.InvalidRegexArguments, GetComparableLocation(memberSyntax), "cultureName"); + return Diagnostic.Create(DiagnosticDescriptors.InvalidRegexArguments, memberSyntax.GetLocation(), "cultureName"); } try @@ -171,7 +171,7 @@ regexPropertySymbol.SetMethod is not null || } catch (CultureNotFoundException) { - return new DiagnosticData(DiagnosticDescriptors.InvalidRegexArguments, GetComparableLocation(memberSyntax), "cultureName"); + return Diagnostic.Create(DiagnosticDescriptors.InvalidRegexArguments, memberSyntax.GetLocation(), "cultureName"); } } @@ -189,13 +189,13 @@ regexPropertySymbol.SetMethod is not null || RegexOptions.Singleline; if ((regexOptions & ~SupportedOptions) != 0) { - return new DiagnosticData(DiagnosticDescriptors.InvalidRegexArguments, GetComparableLocation(memberSyntax), "options"); + return Diagnostic.Create(DiagnosticDescriptors.InvalidRegexArguments, memberSyntax.GetLocation(), "options"); } // Validate the timeout if (matchTimeout is 0 or < -1) { - return new DiagnosticData(DiagnosticDescriptors.InvalidRegexArguments, GetComparableLocation(memberSyntax), "matchTimeout"); + return Diagnostic.Create(DiagnosticDescriptors.InvalidRegexArguments, memberSyntax.GetLocation(), "matchTimeout"); } // Determine the namespace the class is declared in, if any @@ -214,7 +214,7 @@ regexPropertySymbol.SetMethod is not null || var result = new RegexPatternAndSyntax( regexType, IsProperty: regexMemberSymbol is IPropertySymbol, - GetComparableLocation(memberSyntax), + memberSyntax.GetLocation(), regexMemberSymbol.Name, memberSyntax.Modifiers.ToString(), nullableRegex, @@ -246,21 +246,13 @@ SyntaxKind.StructDeclaration or SyntaxKind.RecordDeclaration or SyntaxKind.RecordStructDeclaration or SyntaxKind.InterfaceDeclaration; - - // Get a Location object that doesn't store a reference to the compilation. - // That allows it to compare equally across compilations. - static Location GetComparableLocation(SyntaxNode syntax) - { - var location = syntax.GetLocation(); - return Location.Create(location.SourceTree?.FilePath ?? string.Empty, location.SourceSpan, location.GetLineSpan().Span); - } } /// Data about a regex directly from the GeneratedRegex attribute. internal sealed record RegexPatternAndSyntax(RegexType DeclaringType, bool IsProperty, Location DiagnosticLocation, string MemberName, string Modifiers, bool NullableRegex, string Pattern, RegexOptions Options, int? MatchTimeout, CultureInfo Culture, CompilationData CompilationData); /// Data about a regex, including a fully parsed RegexTree and subsequent analysis. - internal sealed record RegexMethod(RegexType DeclaringType, bool IsProperty, Location DiagnosticLocation, string MemberName, string Modifiers, bool NullableRegex, string Pattern, RegexOptions Options, int? MatchTimeout, RegexTree Tree, AnalysisResults Analysis, CompilationData CompilationData) + internal sealed record RegexMethod(RegexType DeclaringType, bool IsProperty, string MemberName, string Modifiers, bool NullableRegex, string Pattern, RegexOptions Options, int? MatchTimeout, RegexTree Tree, AnalysisResults Analysis, CompilationData CompilationData) { public string? GeneratedName { get; set; } public bool IsDuplicate { get; set; } diff --git a/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.cs b/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.cs index 98e3f1ac35c036..69174c2a508efd 100644 --- a/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.cs +++ b/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.cs @@ -45,10 +45,13 @@ internal record struct CompilationData(bool AllowUnsafe, bool CheckOverflow, Lan public void Initialize(IncrementalGeneratorInitializationContext context) { // Produces one entry per generated regex. This may be: - // - DiagnosticData in the case of a failure that should end the compilation + // - Diagnostic in the case of a failure that should end the compilation // - (RegexMethod regexMethod, string runnerFactoryImplementation, Dictionary requiredHelpers) in the case of valid regex - // - (RegexMethod regexMethod, string reason, DiagnosticData diagnostic) in the case of a limited-support regex - IncrementalValueProvider> results = + // - (RegexMethod regexMethod, string reason, Diagnostic diagnostic) in the case of a limited-support regex + // + // Location is threaded separately from the records so that it doesn't participate in + // record equality — this allows the incremental pipeline to cache results by value. + IncrementalValueProvider<(ImmutableArray Results, ImmutableArray Diagnostics)> collected = context.SyntaxProvider // Find all MethodDeclarationSyntax nodes attributed with GeneratedRegex and gather the required information. @@ -67,8 +70,10 @@ public void Initialize(IncrementalGeneratorInitializationContext context) // Filter out any parsing errors that resulted in null objects being returned. .Where(static m => m is not null) - // The input here will either be a DiagnosticData (in the case of something erroneous detected in GetRegexMethodDataOrFailureDiagnostic) + // The input here will either be a Diagnostic (in the case of something erroneous detected in GetRegexMethodDataOrFailureDiagnostic) // or it will be a RegexPatternAndSyntax containing all of the successfully parsed data from the attribute/method. + // This step parses the regex tree and checks whether full code generation is supported. + // The DiagnosticLocation is consumed here for diagnostic creation and not propagated further. .Select((methodOrDiagnostic, _) => { if (methodOrDiagnostic is RegexPatternAndSyntax method) @@ -77,11 +82,20 @@ public void Initialize(IncrementalGeneratorInitializationContext context) { RegexTree regexTree = RegexParser.Parse(method.Pattern, method.Options | RegexOptions.Compiled, method.Culture); // make sure Compiled is included to get all optimizations applied to it AnalysisResults analysis = RegexTreeAnalyzer.Analyze(regexTree); - return new RegexMethod(method.DeclaringType, method.IsProperty, method.DiagnosticLocation, method.MemberName, method.Modifiers, method.NullableRegex, method.Pattern, method.Options, method.MatchTimeout, regexTree, analysis, method.CompilationData); + RegexMethod regexMethod = new(method.DeclaringType, method.IsProperty, method.MemberName, method.Modifiers, method.NullableRegex, method.Pattern, method.Options, method.MatchTimeout, regexTree, analysis, method.CompilationData); + + // If we're unable to generate a full implementation for this regex, report a diagnostic. + // We'll still output a limited implementation that just caches a new Regex(...). + if (!SupportsCodeGeneration(regexMethod, regexMethod.CompilationData.LanguageVersion, out string? reason)) + { + return (object)(regexMethod, reason, Diagnostic.Create(DiagnosticDescriptors.LimitedSourceGeneration, method.DiagnosticLocation), regexMethod.CompilationData); + } + + return regexMethod; } catch (Exception e) { - return new DiagnosticData(DiagnosticDescriptors.InvalidRegexArguments, method.DiagnosticLocation, e.Message); + return Diagnostic.Create(DiagnosticDescriptors.InvalidRegexArguments, method.DiagnosticLocation, e.Message); } } @@ -93,17 +107,10 @@ public void Initialize(IncrementalGeneratorInitializationContext context) { if (state is not RegexMethod regexMethod) { - Debug.Assert(state is DiagnosticData); + Debug.Assert(state is Diagnostic or ValueTuple); return state; } - // If we're unable to generate a full implementation for this regex, report a diagnostic. - // We'll still output a limited implementation that just caches a new Regex(...). - if (!SupportsCodeGeneration(regexMethod, regexMethod.CompilationData.LanguageVersion, out string? reason)) - { - return (regexMethod, reason, new DiagnosticData(DiagnosticDescriptors.LimitedSourceGeneration, regexMethod.DiagnosticLocation), regexMethod.CompilationData); - } - // Generate the core logic for the regex. Dictionary requiredHelpers = new(); var sw = new StringWriter(); @@ -115,30 +122,47 @@ public void Initialize(IncrementalGeneratorInitializationContext context) return (regexMethod, sw.ToString(), requiredHelpers, regexMethod.CompilationData); }) - // Combine all of the generated text outputs into a single batch. We then generate a single source output from that batch. + // Combine all of the generated text outputs into a single batch, then split + // the source model from diagnostics so they can be emitted independently. .Collect() - - // Apply sequence equality comparison on the result array for incremental caching. - .WithComparer(new ObjectImmutableArraySequenceEqualityComparer()); - - // When there something to output, take all the generated strings and concatenate them to output, - // and raise all of the created diagnostics. - context.RegisterSourceOutput(results, static (context, results) => - { - // Report any top-level diagnostics. - bool allFailures = true; - foreach (object result in results) + .Select(static (results, _) => { - if (result is DiagnosticData d) - { - context.ReportDiagnostic(d.ToDiagnostic()); - } - else + ImmutableArray.Builder? diagnostics = null; + ImmutableArray.Builder? filteredResults = null; + + foreach (object result in results) { - allFailures = false; + if (result is Diagnostic d) + { + (diagnostics ??= ImmutableArray.CreateBuilder()).Add(d); + } + else if (result is ValueTuple limitedSupportResult) + { + (diagnostics ??= ImmutableArray.CreateBuilder()).Add(limitedSupportResult.Item3); + (filteredResults ??= ImmutableArray.CreateBuilder()).Add( + (limitedSupportResult.Item1, limitedSupportResult.Item2, limitedSupportResult.Item4)); + } + else + { + (filteredResults ??= ImmutableArray.CreateBuilder()).Add(result); + } } - } - if (allFailures) + + return ( + Results: filteredResults?.ToImmutable() ?? ImmutableArray.Empty, + Diagnostics: diagnostics?.ToImmutable() ?? ImmutableArray.Empty); + }); + + // Project to just the source model, discarding diagnostics. + // ObjectImmutableArraySequenceEqualityComparer applies element-wise equality over + // the heterogeneous result array, enabling Roslyn's incremental pipeline to skip + // re-emitting source when the model has not changed. + IncrementalValueProvider> sourceModel = + collected.Select(static (t, _) => t.Results).WithComparer(new ObjectImmutableArraySequenceEqualityComparer()); + + context.RegisterSourceOutput(sourceModel, static (context, results) => + { + if (results.IsEmpty) { return; } @@ -168,17 +192,16 @@ public void Initialize(IncrementalGeneratorInitializationContext context) // pair is the implementation used for the key. var emittedExpressions = new Dictionary<(string Pattern, RegexOptions Options, int? Timeout), RegexMethod>(); - // If we have any (RegexMethod regexMethod, string generatedName, string reason, DiagnosticData diagnostic), these are regexes for which we have - // limited support and need to simply output boilerplate. We need to emit their diagnostics. - // If we have any (RegexMethod regexMethod, string generatedName, string runnerFactoryImplementation, Dictionary requiredHelpers), + // If we have any (RegexMethod regexMethod, string reason, CompilationData compilationData), these are regexes for which we have + // limited support and need to simply output boilerplate. + // If we have any (RegexMethod regexMethod, string runnerFactoryImplementation, Dictionary requiredHelpers, CompilationData compilationData), // those are generated implementations to be emitted. We need to gather up their required helpers. Dictionary requiredHelpers = new(); foreach (object? result in results) { RegexMethod? regexMethod = null; - if (result is ValueTuple limitedSupportResult) + if (result is ValueTuple limitedSupportResult) { - context.ReportDiagnostic(limitedSupportResult.Item3.ToDiagnostic()); regexMethod = limitedSupportResult.Item1; } else if (result is ValueTuple, CompilationData> regexImpl) @@ -238,11 +261,11 @@ public void Initialize(IncrementalGeneratorInitializationContext context) writer.Indent++; foreach (object? result in results) { - if (result is ValueTuple limitedSupportResult) + if (result is ValueTuple limitedSupportResult) { if (!limitedSupportResult.Item1.IsDuplicate) { - EmitRegexLimitedBoilerplate(writer, limitedSupportResult.Item1, limitedSupportResult.Item2, limitedSupportResult.Item4.LanguageVersion); + EmitRegexLimitedBoilerplate(writer, limitedSupportResult.Item1, limitedSupportResult.Item2, limitedSupportResult.Item3.LanguageVersion); writer.WriteLine(); } } @@ -290,6 +313,22 @@ public void Initialize(IncrementalGeneratorInitializationContext context) // Save out the source context.AddSource("RegexGenerator.g.cs", sw.ToString()); }); + + // Project to just the diagnostics, discarding the model. ImmutableArray does not + // implement value equality, so Roslyn's incremental pipeline uses reference equality — + // the callback fires on every compilation change. This is by design: diagnostic emission + // is cheap, and we need fresh SourceLocation instances that are pragma-suppressible + // (cf. https://github.com/dotnet/runtime/issues/92509). + IncrementalValueProvider> diagnosticResults = + collected.Select(static (t, _) => t.Diagnostics); + + context.RegisterSourceOutput(diagnosticResults, static (context, diagnostics) => + { + foreach (Diagnostic diagnostic in diagnostics) + { + context.ReportDiagnostic(diagnostic); + } + }); } /// Determines whether the passed in node supports C# code generation. @@ -351,17 +390,6 @@ static bool HasCaseInsensitiveBackReferences(RegexNode node) } } - /// Stores the data necessary to create a Diagnostic. - /// - /// Diagnostics do not have value equality semantics. Storing them in an object model - /// used in the pipeline can result in unnecessary recompilation. - /// - private sealed record class DiagnosticData(DiagnosticDescriptor descriptor, Location location, object? arg = null) - { - /// Create a from the data. - public Diagnostic ToDiagnostic() => Diagnostic.Create(descriptor, location, arg is null ? [] : [arg]); - } - private sealed class ObjectImmutableArraySequenceEqualityComparer : IEqualityComparer> { public bool Equals(ImmutableArray left, ImmutableArray right) diff --git a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/GroupCollectionTests2.cs b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/GroupCollectionTests2.cs index c53ffda29864cd..46953324a43bcc 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/GroupCollectionTests2.cs +++ b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/GroupCollectionTests2.cs @@ -167,7 +167,7 @@ public static void DebuggerAttributeTests() DebuggerAttributeInfo info = DebuggerAttributes.ValidateDebuggerTypeProxyProperties(col); PropertyInfo itemProperty = info.Properties.Single(pr => pr.GetCustomAttribute().State == DebuggerBrowsableState.RootHidden); Group[] items = itemProperty.GetValue(info.Instance) as Group[]; - Assert.Equal(col, items); + Assert.Equal(col.Cast(), items); } [Fact] diff --git a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/Regex.Match.Tests.cs b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/Regex.Match.Tests.cs index c168ed04c54396..af65e57e203a4d 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/Regex.Match.Tests.cs +++ b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/Regex.Match.Tests.cs @@ -385,6 +385,11 @@ public static IEnumerable Match_MemberData() yield return (@"(\(.*?\))+?;", "(a)b);", RegexOptions.None, 0, 6, true, "(a)b);"); yield return (@"(\(.*?\))*;", "(a)b);", RegexOptions.None, 0, 6, true, "(a)b);"); yield return (@"(\(.*?\))*?;", "(a)b);", RegexOptions.None, 0, 6, true, "(a)b);"); + // Lazy quantifier inside optional group where the lazy loop must expand for the rest of the pattern to match. + yield return (@"a(b.*?c)?d", "abccd", RegexOptions.None, 0, 5, true, "abccd"); + yield return (@"a(b.*?c)?d", "abcd", RegexOptions.None, 0, 4, true, "abcd"); + yield return (@"a(b.*?c)?d", "ad", RegexOptions.None, 0, 2, true, "ad"); + yield return (@"a(b.*?c)?d", "abce", RegexOptions.None, 0, 4, false, string.Empty); // Non-backtracking body: optimization correctly applies. These verify bodies without backtracking // inside the outer loop are still correctly matched after the outer loop is made atomic. yield return (@"\w+(\(abc\))?,", "Foo(abc),", RegexOptions.None, 0, 9, true, "Foo(abc),"); diff --git a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/RegexGeneratorHelper.netcoreapp.cs b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/RegexGeneratorHelper.netcoreapp.cs index 677bf16d32ff0a..946b1d2f1be6e9 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/RegexGeneratorHelper.netcoreapp.cs +++ b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/RegexGeneratorHelper.netcoreapp.cs @@ -69,7 +69,7 @@ internal static byte[] CreateAssemblyImage(string source, string assemblyName) throw new InvalidOperationException(); } - private static async Task<(Compilation, GeneratorDriverRunResult)> RunGeneratorCore( + internal static async Task<(Compilation, GeneratorDriverRunResult)> RunGeneratorCore( string code, LanguageVersion langVersion = LanguageVersion.Preview, MetadataReference[]? additionalRefs = null, bool allowUnsafe = false, bool checkOverflow = true, CancellationToken cancellationToken = default) { var proj = new AdhocWorkspace() diff --git a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/RegexGeneratorParserTests.cs b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/RegexGeneratorParserTests.cs index b4d46d8274f57a..3ab5a2923ccf90 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/RegexGeneratorParserTests.cs +++ b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/RegexGeneratorParserTests.cs @@ -3,8 +3,10 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.DotNet.RemoteExecutor; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Globalization; @@ -398,6 +400,26 @@ partial class C Assert.Equal("SYSLIB1043", Assert.Single(diagnostics).Id); } + [Fact] + public async Task Diagnostic_HasPragmaSuppressibleLocation() + { + // SYSLIB1044 (LimitedSourceGeneration) is emitted for case-insensitive backreferences. + string code = """ + #pragma warning disable SYSLIB1044 + using System.Text.RegularExpressions; + partial class C + { + [GeneratedRegex("(a)\\1", RegexOptions.IgnoreCase)] + private static partial Regex Method(); + } + """; + + (Compilation comp, GeneratorDriverRunResult result) = await RegexGeneratorHelper.RunGeneratorCore(code); + var effective = CompilationWithAnalyzers.GetEffectiveDiagnostics(result.Diagnostics, comp); + Diagnostic diagnostic = Assert.Single(effective, d => d.Id == "SYSLIB1044"); + Assert.True(diagnostic.IsSuppressed); + } + [Fact] public async Task Diagnostic_PropertyMustHaveGetter() { diff --git a/src/libraries/System.Threading.Tasks.Parallel/src/System.Threading.Tasks.Parallel.csproj b/src/libraries/System.Threading.Tasks.Parallel/src/System.Threading.Tasks.Parallel.csproj index 66eecbd8f95354..253a06ab94a04f 100644 --- a/src/libraries/System.Threading.Tasks.Parallel/src/System.Threading.Tasks.Parallel.csproj +++ b/src/libraries/System.Threading.Tasks.Parallel/src/System.Threading.Tasks.Parallel.csproj @@ -1,16 +1,13 @@ - $(NetCoreAppCurrent) - $(NetCoreAppCurrent);$(NetCoreAppCurrent)-browser + $(NetCoreAppCurrent) true false $([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) - true - $(DefineConstants);FEATURE_SINGLE_THREADED diff --git a/src/libraries/System.Threading.Thread.WebAssembly.Threading/ref/System.Threading.Thread.WebAssembly.Threading.csproj b/src/libraries/System.Threading.Thread.WebAssembly.Threading/ref/System.Threading.Thread.WebAssembly.Threading.csproj index 074ca5b7f7ede8..027f922c27946a 100644 --- a/src/libraries/System.Threading.Thread.WebAssembly.Threading/ref/System.Threading.Thread.WebAssembly.Threading.csproj +++ b/src/libraries/System.Threading.Thread.WebAssembly.Threading/ref/System.Threading.Thread.WebAssembly.Threading.csproj @@ -2,7 +2,6 @@ $(NetCoreAppCurrent) - true System.Threading.Thread true @@ -11,7 +10,7 @@ false Microsoft true - $(DefineConstants);FEATURE_WASM_MANAGED_THREADS + $(DefineConstants);FEATURE_WASM_MANAGED_THREADS diff --git a/src/libraries/System.Threading.ThreadPool.WebAssembly.Threading/ref/System.Threading.ThreadPool.WebAssembly.Threading.csproj b/src/libraries/System.Threading.ThreadPool.WebAssembly.Threading/ref/System.Threading.ThreadPool.WebAssembly.Threading.csproj index a354d46a0a98bb..08084e8b696248 100644 --- a/src/libraries/System.Threading.ThreadPool.WebAssembly.Threading/ref/System.Threading.ThreadPool.WebAssembly.Threading.csproj +++ b/src/libraries/System.Threading.ThreadPool.WebAssembly.Threading/ref/System.Threading.ThreadPool.WebAssembly.Threading.csproj @@ -3,14 +3,13 @@ System.Threading.ThreadPool true $(NetCoreAppCurrent) - true true false Microsoft true - $(DefineConstants);FEATURE_WASM_MANAGED_THREADS + $(DefineConstants);FEATURE_WASM_MANAGED_THREADS diff --git a/src/libraries/System.Threading.ThreadPool/ref/System.Threading.ThreadPool.csproj b/src/libraries/System.Threading.ThreadPool/ref/System.Threading.ThreadPool.csproj index 5c55020cf70bc1..6b51d22fd072ff 100644 --- a/src/libraries/System.Threading.ThreadPool/ref/System.Threading.ThreadPool.csproj +++ b/src/libraries/System.Threading.ThreadPool/ref/System.Threading.ThreadPool.csproj @@ -2,7 +2,6 @@ $(NetCoreAppCurrent) true - $(DefineConstants);FEATURE_WASM_MANAGED_THREADS diff --git a/src/libraries/System.Threading.WebAssembly.Threading/ref/System.Threading.WebAssembly.Threading.csproj b/src/libraries/System.Threading.WebAssembly.Threading/ref/System.Threading.WebAssembly.Threading.csproj index 63dec7d5e54c2a..844ff1c1cd2c38 100644 --- a/src/libraries/System.Threading.WebAssembly.Threading/ref/System.Threading.WebAssembly.Threading.csproj +++ b/src/libraries/System.Threading.WebAssembly.Threading/ref/System.Threading.WebAssembly.Threading.csproj @@ -2,7 +2,6 @@ $(NetCoreAppCurrent) - true System.Threading true @@ -11,7 +10,7 @@ false Microsoft true - $(DefineConstants);FEATURE_WASM_MANAGED_THREADS + $(DefineConstants);FEATURE_WASM_MANAGED_THREADS diff --git a/src/libraries/System.Threading/src/System.Threading.csproj b/src/libraries/System.Threading/src/System.Threading.csproj index e09ac58c61c410..83d87a841deb1b 100644 --- a/src/libraries/System.Threading/src/System.Threading.csproj +++ b/src/libraries/System.Threading/src/System.Threading.csproj @@ -6,8 +6,7 @@ true true false - true - $(DefineConstants);FEATURE_WASM_MANAGED_THREADS + $(DefineConstants);FEATURE_WASM_MANAGED_THREADS diff --git a/src/libraries/apicompat/ApiCompatBaseline.NetCoreAppLatestStable.xml b/src/libraries/apicompat/ApiCompatBaseline.NetCoreAppLatestStable.xml index 80edf8a7cd7580..962980d5042fc3 100644 --- a/src/libraries/apicompat/ApiCompatBaseline.NetCoreAppLatestStable.xml +++ b/src/libraries/apicompat/ApiCompatBaseline.NetCoreAppLatestStable.xml @@ -1,6 +1,102 @@ + + CP0002 + M:System.Runtime.Intrinsics.Arm.Sve.GatherVectorUInt32WithByteOffsetsZeroExtend(System.Numerics.Vector{System.Int32},System.UInt32*,System.Numerics.Vector{System.Int32}) + net10.0/System.Runtime.Intrinsics.dll + net11.0/System.Runtime.Intrinsics.dll + + + CP0002 + M:System.Runtime.Intrinsics.Arm.Sve.GatherVectorUInt32WithByteOffsetsZeroExtend(System.Numerics.Vector{System.Int32},System.UInt32*,System.Numerics.Vector{System.UInt32}) + net10.0/System.Runtime.Intrinsics.dll + net11.0/System.Runtime.Intrinsics.dll + + + CP0002 + M:System.Runtime.Intrinsics.Arm.Sve.GatherVectorUInt32WithByteOffsetsZeroExtend(System.Numerics.Vector{System.UInt32},System.UInt32*,System.Numerics.Vector{System.Int32}) + net10.0/System.Runtime.Intrinsics.dll + net11.0/System.Runtime.Intrinsics.dll + + + CP0002 + M:System.Runtime.Intrinsics.Arm.Sve.GatherVectorUInt32WithByteOffsetsZeroExtend(System.Numerics.Vector{System.UInt32},System.UInt32*,System.Numerics.Vector{System.UInt32}) + net10.0/System.Runtime.Intrinsics.dll + net11.0/System.Runtime.Intrinsics.dll + + + CP0002 + M:System.Runtime.Intrinsics.Arm.Sve.GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting(System.Numerics.Vector{System.Int32},System.UInt32*,System.Numerics.Vector{System.Int32}) + net10.0/System.Runtime.Intrinsics.dll + net11.0/System.Runtime.Intrinsics.dll + + + CP0002 + M:System.Runtime.Intrinsics.Arm.Sve.GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting(System.Numerics.Vector{System.Int32},System.UInt32*,System.Numerics.Vector{System.UInt32}) + net10.0/System.Runtime.Intrinsics.dll + net11.0/System.Runtime.Intrinsics.dll + + + CP0002 + M:System.Runtime.Intrinsics.Arm.Sve.GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting(System.Numerics.Vector{System.UInt32},System.UInt32*,System.Numerics.Vector{System.Int32}) + net10.0/System.Runtime.Intrinsics.dll + net11.0/System.Runtime.Intrinsics.dll + + + CP0002 + M:System.Runtime.Intrinsics.Arm.Sve.GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting(System.Numerics.Vector{System.UInt32},System.UInt32*,System.Numerics.Vector{System.UInt32}) + net10.0/System.Runtime.Intrinsics.dll + net11.0/System.Runtime.Intrinsics.dll + + + CP0002 + M:System.Runtime.Intrinsics.Arm.Sve.GatherVectorUInt32ZeroExtend(System.Numerics.Vector{System.Int32},System.UInt32*,System.Numerics.Vector{System.Int32}) + net10.0/System.Runtime.Intrinsics.dll + net11.0/System.Runtime.Intrinsics.dll + + + CP0002 + M:System.Runtime.Intrinsics.Arm.Sve.GatherVectorUInt32ZeroExtend(System.Numerics.Vector{System.Int32},System.UInt32*,System.Numerics.Vector{System.UInt32}) + net10.0/System.Runtime.Intrinsics.dll + net11.0/System.Runtime.Intrinsics.dll + + + CP0002 + M:System.Runtime.Intrinsics.Arm.Sve.GatherVectorUInt32ZeroExtend(System.Numerics.Vector{System.UInt32},System.UInt32*,System.Numerics.Vector{System.Int32}) + net10.0/System.Runtime.Intrinsics.dll + net11.0/System.Runtime.Intrinsics.dll + + + CP0002 + M:System.Runtime.Intrinsics.Arm.Sve.GatherVectorUInt32ZeroExtend(System.Numerics.Vector{System.UInt32},System.UInt32*,System.Numerics.Vector{System.UInt32}) + net10.0/System.Runtime.Intrinsics.dll + net11.0/System.Runtime.Intrinsics.dll + + + CP0002 + M:System.Runtime.Intrinsics.Arm.Sve.GatherVectorUInt32ZeroExtendFirstFaulting(System.Numerics.Vector{System.Int32},System.UInt32*,System.Numerics.Vector{System.Int32}) + net10.0/System.Runtime.Intrinsics.dll + net11.0/System.Runtime.Intrinsics.dll + + + CP0002 + M:System.Runtime.Intrinsics.Arm.Sve.GatherVectorUInt32ZeroExtendFirstFaulting(System.Numerics.Vector{System.Int32},System.UInt32*,System.Numerics.Vector{System.UInt32}) + net10.0/System.Runtime.Intrinsics.dll + net11.0/System.Runtime.Intrinsics.dll + + + CP0002 + M:System.Runtime.Intrinsics.Arm.Sve.GatherVectorUInt32ZeroExtendFirstFaulting(System.Numerics.Vector{System.UInt32},System.UInt32*,System.Numerics.Vector{System.Int32}) + net10.0/System.Runtime.Intrinsics.dll + net11.0/System.Runtime.Intrinsics.dll + + + CP0002 + M:System.Runtime.Intrinsics.Arm.Sve.GatherVectorUInt32ZeroExtendFirstFaulting(System.Numerics.Vector{System.UInt32},System.UInt32*,System.Numerics.Vector{System.UInt32}) + net10.0/System.Runtime.Intrinsics.dll + net11.0/System.Runtime.Intrinsics.dll + CP0002 M:System.Runtime.Intrinsics.Arm.Sve2.AddSaturateWithSignedAddend(System.Numerics.Vector{System.Byte},System.Numerics.Vector{System.SByte}) diff --git a/src/libraries/pretest.proj b/src/libraries/pretest.proj index 3715486ce1c477..8ec39373a5d998 100644 --- a/src/libraries/pretest.proj +++ b/src/libraries/pretest.proj @@ -142,7 +142,6 @@ - - $(DefineConstants);MONO_FEATURE_SRE true - true - true - true - true + true + true + true true true @@ -121,8 +120,6 @@ $(DefineConstants);FEATURE_PERFTRACING $(DefineConstants);FEATURE_OBJCMARSHAL - $(DefineConstants);FEATURE_WASM_MANAGED_THREADS - $(DefineConstants);FEATURE_SINGLE_THREADED diff --git a/src/mono/wasm/Wasm.Build.Tests/Common/TestUtils.cs b/src/mono/wasm/Wasm.Build.Tests/Common/TestUtils.cs index aed3fbc235c8b5..2b01e61dd8ebf1 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Common/TestUtils.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Common/TestUtils.cs @@ -95,7 +95,7 @@ public static void AssertEqual(object expected, object actual, string label) return; throw EqualException.ForMismatchedValues( - expected, actual, + $"{expected}", $"{actual}", $"[{label}]\n"); } diff --git a/src/mono/wasm/testassets/BlazorBasicTestApp/App/Layout/NavMenu.razor b/src/mono/wasm/testassets/BlazorBasicTestApp/App/Layout/NavMenu.razor index 345ee43c02f805..3fbc1800325745 100644 --- a/src/mono/wasm/testassets/BlazorBasicTestApp/App/Layout/NavMenu.razor +++ b/src/mono/wasm/testassets/BlazorBasicTestApp/App/Layout/NavMenu.razor @@ -28,7 +28,7 @@ @code { - private bool collapseNavMenu = true; + private bool collapseNavMenu = false; private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null; diff --git a/src/mono/wasm/threads.md b/src/mono/wasm/threads.md index d5aff1239e39c9..2931e6172738c2 100644 --- a/src/mono/wasm/threads.md +++ b/src/mono/wasm/threads.md @@ -4,20 +4,19 @@ For WebAssembly documentation including building, testing, and debugging, see [W ## Building the runtime -Build the runtime with `/p:WasmEnableThreads=true` to enable support for multi-threading. +Build the runtime with `/p:WasmEnableThreads=true` to enable experimental support for multi-threading. ## Building sample apps ## Sample apps use the "public" properties `WasmEnableThreads` to enable the relevant functionality. -This also works with released versions of .NET 7 or later and the `wasmbrowser` template. ## Libraries feature defines ## -We use the `FeatureWasmManagedThreads` property in the libraries projects to conditionally define +We use the `WasmEnableThreads` property in the libraries projects to conditionally define `FEATURE_WASM_MANAGED_THREADS` which is used to affect how the libraries are built for the multi-threaded runtime. -### Ref asssemblies ### +### Ref assemblies ### For ref assemblies that have APIs that are related to threading, we use `[UnsupportedOSPlatform("browser")]` under a `FEATURE_WASM_MANAGED_THREADS` define to mark APIs that are not @@ -28,6 +27,7 @@ the single-threaded ref assemblies, and assemblies. By default users compile against the single-threaded ref assemblies, but by adding a `PackageReference` to `Microsoft.NET.WebAssembly.Threading`, they get the multi-threaded ref assemblies. +This fork of reference assemblies exists just for the experimental support. It will have to be undone if/once wasm threads are supported for real. ### Implementation assemblies ### diff --git a/src/native/libs/Common/JavaScript/loader/assets.ts b/src/native/libs/Common/JavaScript/loader/assets.ts index 04e160eb2aefaa..8a0f73f49e9561 100644 --- a/src/native/libs/Common/JavaScript/loader/assets.ts +++ b/src/native/libs/Common/JavaScript/loader/assets.ts @@ -3,7 +3,7 @@ import type { JsModuleExports, JsAsset, AssemblyAsset, WasmAsset, IcuAsset, EmscriptenModuleInternal, WebAssemblyBootResourceType, AssetEntryInternal, PromiseCompletionSource, LoadBootResourceCallback, InstantiateWasmSuccessCallback, SymbolsAsset } from "./types"; -import { dotnetAssert, dotnetLogger, dotnetInternals, dotnetBrowserHostExports, dotnetUpdateInternals, Module, dotnetDiagnosticsExports, dotnetNativeBrowserExports } from "./cross-module"; +import { dotnetAssert, dotnetLogger, dotnetInternals, dotnetBrowserHostExports, dotnetUpdateInternals, Module, dotnetDiagnosticsExports, dotnetNativeBrowserExports, dotnetApi } from "./cross-module"; import { ENVIRONMENT_IS_SHELL, ENVIRONMENT_IS_NODE, browserVirtualAppBase } from "./per-module"; import { createPromiseCompletionSource, delay } from "./promise-completion-source"; import { locateFile, makeURLAbsoluteWithApplicationBase } from "./bootstrap"; @@ -55,6 +55,37 @@ export async function loadJSModule(asset: JsAsset): Promise { return mod; } +export async function callLibraryInitializerOnRuntimeConfigLoaded(asset: JsAsset): Promise { + const module = await loadJSModule(asset); + const name = asset.name || asset.resolvedUrl || "unknown"; + try { + if (typeof module.onRuntimeConfigLoaded === "function") { + await module.onRuntimeConfigLoaded(loaderConfig); + } else if (typeof module.onRuntimeReady !== "function") { + dotnetLogger.warn(`Module '${name}' does not export 'onRuntimeConfigLoaded' function. Make sure the module initializer is correctly defined and exported.`); + } + return module; + } catch (err) { + const message = err instanceof Error ? err.message : String(err); + throw new Error(`Failed to invoke 'onRuntimeConfigLoaded' on library initializer '${name}': ${message}`, { cause: err }); + } +} + +export async function callLibraryInitializerOnRuntimeReady([asset, modulePromise]: [JsAsset, Promise]): Promise { + const module = await modulePromise; + const name = asset.name || asset.resolvedUrl || "unknown"; + try { + if (typeof module.onRuntimeReady === "function") { + await module.onRuntimeReady(dotnetApi); + } else if (typeof module.onRuntimeConfigLoaded !== "function") { + dotnetLogger.warn(`Module '${name}' does not export 'onRuntimeReady' function. Make sure the module initializer is correctly defined and exported.`); + } + } catch (err) { + const message = err instanceof Error ? err.message : String(err); + throw new Error(`Failed to invoke 'onRuntimeReady' on library initializer '${name}': ${message}`, { cause: err }); + } +} + export function fetchMainWasm(asset: WasmAsset): Promise { totalAssetsToDownload++; const assetInternal = asset as AssetEntryInternal; diff --git a/src/native/libs/Common/JavaScript/loader/run.ts b/src/native/libs/Common/JavaScript/loader/run.ts index 83e42b1576236b..ea955d7f500aa6 100644 --- a/src/native/libs/Common/JavaScript/loader/run.ts +++ b/src/native/libs/Common/JavaScript/loader/run.ts @@ -1,31 +1,19 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -import type { JsModuleExports, EmscriptenModuleInternal } from "./types"; +import type { JsModuleExports, EmscriptenModuleInternal, JsAsset } from "./types"; -import { dotnetAssert, dotnetInternals, dotnetBrowserHostExports, dotnetApi, Module } from "./cross-module"; +import { dotnetAssert, dotnetInternals, dotnetBrowserHostExports, Module } from "./cross-module"; import { exit, runtimeState } from "./exit"; import { createPromiseCompletionSource } from "./promise-completion-source"; import { getIcuResourceName } from "./icu"; import { loaderConfig, validateLoaderConfig } from "./config"; -import { fetchAssembly, fetchIcu, fetchNativeSymbols, fetchPdb, fetchSatelliteAssemblies, fetchVfs, fetchMainWasm, loadDotnetModule, loadJSModule, nativeModulePromiseController, verifyAllAssetsDownloaded } from "./assets"; +import { fetchAssembly, fetchIcu, fetchNativeSymbols, fetchPdb, fetchSatelliteAssemblies, fetchVfs, fetchMainWasm, loadDotnetModule, loadJSModule, nativeModulePromiseController, verifyAllAssetsDownloaded, callLibraryInitializerOnRuntimeReady, callLibraryInitializerOnRuntimeConfigLoaded } from "./assets"; import { initPolyfills } from "./polyfills"; import { validateEngineFeatures } from "./bootstrap"; const runMainPromiseController = createPromiseCompletionSource(); -async function callLibraryInitializers(modules: JsModuleExports[], resources: any[], methodName: string, args: any): Promise { - await Promise.all(modules.map(async (module, i) => { - try { - await (module as any)[methodName]?.(args); - } catch (err) { - const name = (resources[i] as any).name || "unknown"; - const message = err instanceof Error ? err.message : String(err); - throw new Error(`Failed to invoke '${methodName}' on library initializer '${name}': ${message}`, { cause: err }); - } - })); -} - // WASM-TODO: downloadOnly - Blazor render mode auto pre-download. Really no start. // WASM-TODO: debugLevel @@ -35,6 +23,7 @@ export async function createRuntime(downloadOnly: boolean): Promise { if (!loaderConfig.resources || !loaderConfig.resources.coreAssembly || !loaderConfig.resources.coreAssembly.length) throw new Error("Invalid config, resources is not set"); try { runtimeState.creatingRuntime = true; + const resources = loaderConfig.resources; await validateEngineFeatures(); @@ -43,42 +32,40 @@ export async function createRuntime(downloadOnly: boolean): Promise { } validateLoaderConfig(); - const afterConfigLoadedResources = loaderConfig.resources.modulesAfterConfigLoaded || []; - const modulesAfterConfigLoaded = await Promise.all(afterConfigLoadedResources.map(loadJSModule)); - await callLibraryInitializers(modulesAfterConfigLoaded, afterConfigLoadedResources, "onRuntimeConfigLoaded", loaderConfig); + const modulesAfterConfigLoadedPromises: [JsAsset, Promise][] = normalizeCollection(resources.modulesAfterConfigLoaded).map((a) => [a, callLibraryInitializerOnRuntimeConfigLoaded(a)]); + await Promise.all(modulesAfterConfigLoadedPromises.map(([, p]) => p)); - // after onConfigLoaded hooks, polyfills can be initialized + // after onConfigLoaded hooks that could install polyfills, our polyfills can be initialized await initPolyfills(); - if (loaderConfig.resources.jsModuleDiagnostics && loaderConfig.resources.jsModuleDiagnostics.length > 0) { - const diagnosticsModule = await loadDotnetModule(loaderConfig.resources.jsModuleDiagnostics[0]); + if (resources.jsModuleDiagnostics && resources.jsModuleDiagnostics.length > 0) { + const diagnosticsModule = await loadDotnetModule(resources.jsModuleDiagnostics[0]); diagnosticsModule.dotnetInitializeModule(dotnetInternals); - if (loaderConfig.resources.wasmSymbols && loaderConfig.resources.wasmSymbols.length > 0) { - await fetchNativeSymbols(loaderConfig.resources.wasmSymbols[0]); + if (resources.wasmSymbols && resources.wasmSymbols.length > 0) { + await fetchNativeSymbols(resources.wasmSymbols[0]); } } - const nativeModulePromise: Promise = loadDotnetModule(loaderConfig.resources.jsModuleNative[0]); - const runtimeModulePromise: Promise = loadDotnetModule(loaderConfig.resources.jsModuleRuntime[0]); - const wasmNativePromise: Promise = fetchMainWasm(loaderConfig.resources.wasmNative[0]); + const nativeModulePromise: Promise = loadDotnetModule(resources.jsModuleNative[0]); + const runtimeModulePromise: Promise = loadDotnetModule(resources.jsModuleRuntime[0]); + const wasmNativePromise: Promise = fetchMainWasm(resources.wasmNative[0]); - const coreAssembliesPromise = Promise.all(loaderConfig.resources.coreAssembly.map(fetchAssembly)); - const coreVfsPromise = Promise.all((loaderConfig.resources.coreVfs || []).map(fetchVfs)); + const coreAssembliesPromise = forEachResource(resources.coreAssembly, fetchAssembly); + const coreVfsPromise = forEachResource(resources.coreVfs, fetchVfs); const icuResourceName = getIcuResourceName(); - const icuDataPromise = icuResourceName ? Promise.all((loaderConfig.resources.icu || []).filter(asset => asset.name === icuResourceName).map(fetchIcu)) : Promise.resolve([]); + const icuDataPromise = forEachResource(resources.icu, fetchIcu, asset => asset.name === icuResourceName); - const assembliesPromise = Promise.all(loaderConfig.resources.assembly.map(fetchAssembly)); - const satelliteResourcesPromise = loaderConfig.loadAllSatelliteResources && loaderConfig.resources.satelliteResources - ? fetchSatelliteAssemblies(Object.keys(loaderConfig.resources.satelliteResources)) + const assembliesPromise = forEachResource(resources.assembly, fetchAssembly); + const satelliteResourcesPromise = loaderConfig.loadAllSatelliteResources && resources.satelliteResources + ? fetchSatelliteAssemblies(Object.keys(resources.satelliteResources)) : Promise.resolve(); - const vfsPromise = Promise.all((loaderConfig.resources.vfs || []).map(fetchVfs)); + const vfsPromise = forEachResource(resources.vfs, fetchVfs); // WASM-TODO: also check that the debugger is linked in and check feature flags const isDebuggingSupported = loaderConfig.debugLevel != 0; - const corePDBsPromise = isDebuggingSupported ? Promise.all((loaderConfig.resources.corePdb || []).map(fetchPdb)) : Promise.resolve([]); - const pdbsPromise = isDebuggingSupported ? Promise.all((loaderConfig.resources.pdb || []).map(fetchPdb)) : Promise.resolve([]); - const afterRuntimeReadyResources = loaderConfig.resources.modulesAfterRuntimeReady || []; - const modulesAfterRuntimeReadyPromise = Promise.all(afterRuntimeReadyResources.map(loadJSModule)); + const corePDBsPromise = forEachResource(resources.corePdb, fetchPdb, () => isDebuggingSupported); + const pdbsPromise = forEachResource(resources.pdb, fetchPdb, () => isDebuggingSupported); + const modulesAfterRuntimeReadyPromises: [JsAsset, Promise][] = normalizeCollection(resources.modulesAfterRuntimeReady).map((a) => [a, loadJSModule(a)]); const nativeModule = await nativeModulePromise; const modulePromise = nativeModule.dotnetInitializeModule(dotnetInternals); @@ -94,6 +81,7 @@ export async function createRuntime(downloadOnly: boolean): Promise { await vfsPromise; await icuDataPromise; await wasmNativePromise; // this is just to propagate errors + if (!downloadOnly) { Module.runtimeKeepalivePush(); await initializeCoreCLR(); @@ -107,18 +95,20 @@ export async function createRuntime(downloadOnly: boolean): Promise { verifyAllAssetsDownloaded(); - if (!downloadOnly) { - if (typeof Module.onDotnetReady === "function") { - await Module.onDotnetReady(); - } - const modulesAfterRuntimeReady = await modulesAfterRuntimeReadyPromise; - const allRuntimeReadyModules = [...modulesAfterConfigLoaded, ...modulesAfterRuntimeReady]; - const allRuntimeReadyResources = [...afterConfigLoadedResources, ...afterRuntimeReadyResources]; - await callLibraryInitializers(allRuntimeReadyModules, allRuntimeReadyResources, "onRuntimeReady", dotnetApi); + if (downloadOnly) { + return; } - runtimeState.creatingRuntime = false; + + if (typeof Module.onDotnetReady === "function") { + await Module.onDotnetReady(); + } + + await Promise.all([...modulesAfterConfigLoadedPromises, ...modulesAfterRuntimeReadyPromises].map(callLibraryInitializerOnRuntimeReady)); + } catch (err) { exit(1, err); + } finally { + runtimeState.creatingRuntime = false; } } export function abortStartup(reason: any): void { @@ -150,4 +140,17 @@ export function getRunMainPromise(): Promise { return runMainPromiseController.promise; } +function forEachResource(collection: T[] | undefined, callback: (item: T) => Promise, filter?: (item: T) => boolean): Promise { + if (!collection) { + return Promise.resolve([]); + } + const filteredCollection = filter ? collection.filter(filter) : collection; + return Promise.all(filteredCollection.map(callback)); +} +function normalizeCollection(collection: T[] | undefined): T[] { + if (!collection) { + return []; + } + return collection; +} diff --git a/src/native/libs/System.Native/entrypoints.c b/src/native/libs/System.Native/entrypoints.c index 428c06603470a8..458980b89bd75e 100644 --- a/src/native/libs/System.Native/entrypoints.c +++ b/src/native/libs/System.Native/entrypoints.c @@ -104,11 +104,13 @@ static const Entry s_sysNative[] = DllImportEntry(SystemNative_PosixFAdvise) DllImportEntry(SystemNative_FAllocate) DllImportEntry(SystemNative_Read) + DllImportEntry(SystemNative_ReadFromNonblocking) DllImportEntry(SystemNative_ReadLink) DllImportEntry(SystemNative_Rename) DllImportEntry(SystemNative_RmDir) DllImportEntry(SystemNative_Sync) DllImportEntry(SystemNative_Write) + DllImportEntry(SystemNative_WriteToNonblocking) DllImportEntry(SystemNative_CopyFile) DllImportEntry(SystemNative_INotifyInit) DllImportEntry(SystemNative_INotifyAddWatch) diff --git a/src/native/libs/System.Native/pal_interfaceaddresses.c b/src/native/libs/System.Native/pal_interfaceaddresses.c index e81e2110860b95..bda4817e88adc0 100644 --- a/src/native/libs/System.Native/pal_interfaceaddresses.c +++ b/src/native/libs/System.Native/pal_interfaceaddresses.c @@ -304,6 +304,9 @@ int32_t SystemNative_EnumerateInterfaceAddresses(void* context, #endif } +// See entriesCount, calloc() below. +c_static_assert(sizeof(NetworkInterfaceInfo) >= sizeof(IpAddressInfo)); + int32_t SystemNative_GetNetworkInterfaces(int32_t * interfaceCount, NetworkInterfaceInfo **interfaceList, int32_t * addressCount, IpAddressInfo **addressList ) { #ifdef ANDROID_GETIFADDRS_WORKAROUND @@ -352,16 +355,13 @@ int32_t SystemNative_GetNetworkInterfaces(int32_t * interfaceCount, NetworkInter // Allocate estimated space. It can be little bit more than we need. // To save allocation need for separate free() we will allocate one memory chunk // where we first write out NetworkInterfaceInfo entries immediately followed by - // IpAddressInfo list. -#ifdef TARGET_ANDROID - // Since Android API 30, getifaddrs returns only AF_INET and AF_INET6 addresses and we do not - // get any AF_PACKET addresses and so count == ip4count + ip6count. We need to make sure that - // the memoryBlock is large enough to hold all interfaces (up to `count` entries) and all - // addresses (ip4count + ip6count) without any overlap between interfaceList and addressList. + // IpAddressInfo list. Make sure we get enough space for both the list of + // NetworkInterfaceInfo[count] and IpAddressInfo[ip4count + ip6count] + // Make no assumptions about how many ip4count + ip6count there may be. + // This does assume sizeof(NetworkInterfaceInfo) >= sizeof(IpAddressInfo) + // which is checked in an assert above this function. + int entriesCount = count + ip4count + ip6count; -#else - int entriesCount = count; -#endif void * memoryBlock = calloc((size_t)entriesCount, sizeof(NetworkInterfaceInfo)); if (memoryBlock == NULL) { @@ -373,7 +373,7 @@ int32_t SystemNative_GetNetworkInterfaces(int32_t * interfaceCount, NetworkInter ifaddrsEntry = head; *interfaceList = nii = (NetworkInterfaceInfo*)memoryBlock; // address of first IpAddressInfo after all NetworkInterfaceInfo entries. - *addressList = ai = (IpAddressInfo*)(nii + (entriesCount - ip4count - ip6count)); + *addressList = ai = (IpAddressInfo*)(nii + count); while (ifaddrsEntry != NULL) { diff --git a/src/native/libs/System.Native/pal_io.c b/src/native/libs/System.Native/pal_io.c index 31aa7d87f97c27..cd488c01c2c339 100644 --- a/src/native/libs/System.Native/pal_io.c +++ b/src/native/libs/System.Native/pal_io.c @@ -569,32 +569,32 @@ int32_t SystemNative_Pipe(int32_t pipeFds[2], int32_t flags) errno = ENOTSUP; return -1; #else // TARGET_WASM - switch (flags) + if ((flags & ~(PAL_O_CLOEXEC | PAL_O_NONBLOCK_READ | PAL_O_NONBLOCK_WRITE)) != 0) + { + assert_msg(false, "Unknown pipe flag", (int)flags); + errno = EINVAL; + return -1; + } + + int32_t pipeFlags = 0; + if ((flags & PAL_O_CLOEXEC) != 0) { - case 0: - break; - case PAL_O_CLOEXEC: #if HAVE_O_CLOEXEC - flags = O_CLOEXEC; + pipeFlags = O_CLOEXEC; #endif - break; - default: - assert_msg(false, "Unknown pipe flag", (int)flags); - errno = EINVAL; - return -1; } int32_t result; #if HAVE_PIPE2 // If pipe2 is available, use it. This will handle O_CLOEXEC if it was set. - while ((result = pipe2(pipeFds, flags)) < 0 && errno == EINTR); + while ((result = pipe2(pipeFds, pipeFlags)) < 0 && errno == EINTR); #elif HAVE_PIPE // Otherwise, use pipe. while ((result = pipe(pipeFds)) < 0 && errno == EINTR); // Then, if O_CLOEXEC was specified, use fcntl to configure the file descriptors appropriately. #if HAVE_O_CLOEXEC - if ((flags & O_CLOEXEC) != 0 && result == 0) + if ((pipeFlags & O_CLOEXEC) != 0 && result == 0) #else if ((flags & PAL_O_CLOEXEC) != 0 && result == 0) #endif @@ -616,6 +616,28 @@ int32_t SystemNative_Pipe(int32_t pipeFds[2], int32_t flags) #else /* HAVE_PIPE */ result = -1; #endif /* HAVE_PIPE */ + + if (result == 0 && ((flags & (PAL_O_NONBLOCK_READ | PAL_O_NONBLOCK_WRITE)) != 0)) + { + if ((flags & PAL_O_NONBLOCK_READ) != 0) + { + result = SystemNative_FcntlSetIsNonBlocking((intptr_t)pipeFds[0], 1); + } + + if (result == 0 && (flags & PAL_O_NONBLOCK_WRITE) != 0) + { + result = SystemNative_FcntlSetIsNonBlocking((intptr_t)pipeFds[1], 1); + } + + if (result != 0) + { + int tmpErrno = errno; + close(pipeFds[0]); + close(pipeFds[1]); + errno = tmpErrno; + } + } + return result; #endif // TARGET_WASM } @@ -1210,6 +1232,67 @@ int32_t SystemNative_Read(intptr_t fd, void* buffer, int32_t bufferSize) return Common_Read(fd, buffer, bufferSize); } +int32_t SystemNative_ReadFromNonblocking(intptr_t fd, void* buffer, int32_t bufferSize) +{ + while (1) + { + int32_t result = Common_Read(fd, buffer, bufferSize); + if (result != -1 || (errno != EAGAIN && errno != EWOULDBLOCK)) + { + return result; + } + + // The fd is non-blocking and no data is available yet. + // Block (on a thread pool thread) until data arrives or the pipe/socket is closed. + PollEvent pollEvent = { .FileDescriptor = (int32_t)fd, .Events = PAL_POLLIN, .TriggeredEvents = 0 }; + uint32_t triggered = 0; + int32_t pollResult = Common_Poll(&pollEvent, 1, -1, &triggered); + if (pollResult != Error_SUCCESS) + { + errno = ConvertErrorPalToPlatform(pollResult); + return -1; + } + + if ((pollEvent.TriggeredEvents & (PAL_POLLHUP | PAL_POLLERR)) != 0 && + (pollEvent.TriggeredEvents & PAL_POLLIN) == 0) + { + // The pipe/socket was closed with no data available (EOF). + return 0; + } + } +} + +int32_t SystemNative_WriteToNonblocking(intptr_t fd, const void* buffer, int32_t bufferSize) +{ + while (1) + { + int32_t result = Common_Write(fd, buffer, bufferSize); + if (result != -1 || (errno != EAGAIN && errno != EWOULDBLOCK)) + { + return result; + } + + // The fd is non-blocking and the write buffer is full. + // Block (on a thread pool thread) until space is available or the pipe/socket is closed. + PollEvent pollEvent = { .FileDescriptor = (int32_t)fd, .Events = PAL_POLLOUT, .TriggeredEvents = 0 }; + uint32_t triggered = 0; + int32_t pollResult = Common_Poll(&pollEvent, 1, -1, &triggered); + if (pollResult != Error_SUCCESS) + { + errno = ConvertErrorPalToPlatform(pollResult); + return -1; + } + + if ((pollEvent.TriggeredEvents & (PAL_POLLHUP | PAL_POLLERR)) != 0 && + (pollEvent.TriggeredEvents & PAL_POLLOUT) == 0) + { + // The pipe/socket was closed. + errno = EPIPE; + return -1; + } + } +} + int32_t SystemNative_ReadLink(const char* path, char* buffer, int32_t bufferSize) { assert(buffer != NULL || bufferSize == 0); diff --git a/src/native/libs/System.Native/pal_io.h b/src/native/libs/System.Native/pal_io.h index 3d94e28bd1113d..bc6c108828d256 100644 --- a/src/native/libs/System.Native/pal_io.h +++ b/src/native/libs/System.Native/pal_io.h @@ -180,6 +180,8 @@ enum PAL_O_TRUNC = 0x0080, // Truncate file to length 0 if it already exists PAL_O_SYNC = 0x0100, // Block writes call will block until physically written PAL_O_NOFOLLOW = 0x0200, // Fails to open the target if it's a symlink, parent symlinks are allowed + PAL_O_NONBLOCK_READ = 0x0400, // Set O_NONBLOCK on the read end of a pipe + PAL_O_NONBLOCK_WRITE = 0x0800, // Set O_NONBLOCK on the write end of a pipe }; /** @@ -439,12 +441,11 @@ PALEXPORT int32_t SystemNative_CloseDir(DIR* dir); /** * Creates a pipe. Implemented as shim to pipe(2) or pipe2(2) if available. - * Flags are ignored if pipe2 is not available. * * Returns 0 for success, -1 for failure. Sets errno on failure. */ PALEXPORT int32_t SystemNative_Pipe(int32_t pipefd[2], // [out] pipefds[0] gets read end, pipefd[1] gets write end. - int32_t flags); // 0 for defaults or PAL_O_CLOEXEC for close-on-exec + int32_t flags); // 0 for defaults. Use PAL_O_CLOEXEC, PAL_O_NONBLOCK_READ, and PAL_O_NONBLOCK_WRITE for additional behavior. // NOTE: Rather than a general fcntl shim, we opt to export separate functions // for each command. This allows use to have strongly typed arguments and saves @@ -704,6 +705,14 @@ PALEXPORT int32_t SystemNative_FAllocate(intptr_t fd, int64_t offset, int64_t le */ PALEXPORT int32_t SystemNative_Read(intptr_t fd, void* buffer, int32_t bufferSize); +/** + * Reads the number of bytes specified into the provided buffer from the specified, opened non-blocking file descriptor. + * If no data is currently available, polls the file descriptor until data arrives or the pipe/socket is closed. + * + * Returns the number of bytes read on success; 0 on EOF; otherwise, -1 is returned and errno is set. + */ +PALEXPORT int32_t SystemNative_ReadFromNonblocking(intptr_t fd, void* buffer, int32_t bufferSize); + /** * Takes a path to a symbolic link and attempts to place the link target path into the buffer. If the buffer is too * small, the path will be truncated. No matter what, the buffer will not be null terminated. @@ -739,6 +748,14 @@ PALEXPORT void SystemNative_Sync(void); */ PALEXPORT int32_t SystemNative_Write(intptr_t fd, const void* buffer, int32_t bufferSize); +/** + * Writes the specified buffer to the provided open non-blocking file descriptor. + * If the write buffer is currently full, polls the file descriptor until space is available or the pipe/socket is closed. + * + * Returns the number of bytes written on success; otherwise, returns -1 and sets errno. + */ +PALEXPORT int32_t SystemNative_WriteToNonblocking(intptr_t fd, const void* buffer, int32_t bufferSize); + /** * Copies all data from the source file descriptor to the destination file descriptor. * diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IBuiltInCOM.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IBuiltInCOM.cs index 98391986d0fda9..a355c5601946f9 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IBuiltInCOM.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IBuiltInCOM.cs @@ -30,6 +30,8 @@ public interface IBuiltInCOM : IContract // ccw may be any ComCallWrapper in the chain; the implementation navigates to the start. IEnumerable GetCCWInterfaces(TargetPointer ccw) => throw new NotImplementedException(); IEnumerable GetRCWCleanupList(TargetPointer cleanupListPtr) => throw new NotImplementedException(); + IEnumerable<(TargetPointer MethodTable, TargetPointer Unknown)> GetRCWInterfaces(TargetPointer rcw) => throw new NotImplementedException(); + TargetPointer GetRCWContext(TargetPointer rcw) => throw new NotImplementedException(); } public readonly struct BuiltInCOM : IBuiltInCOM diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IExecutionManager.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IExecutionManager.cs index fd9e9d97f15539..d5fb9fb7622ccd 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IExecutionManager.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IExecutionManager.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Generic; namespace Microsoft.Diagnostics.DataContractReader.Contracts; @@ -12,6 +13,28 @@ public struct CodeBlockHandle public CodeBlockHandle(TargetPointer address) => Address = address; } +public struct ExceptionClauseInfo +{ + public enum ExceptionClauseFlags : uint + { + Unknown = 0, + Fault = 0x1, + Finally = 0x2, + Filter = 0x3, + Typed = 0x4 + } + public ExceptionClauseFlags ClauseType; + public bool? IsCatchAllHandler; + public uint TryStartPC; + public uint TryEndPC; + public uint HandlerStartPC; + public uint HandlerEndPC; + public uint? FilterOffset; + public uint? ClassToken; + public TargetNUInt? TypeHandle; + public TargetPointer? ModuleAddr; +} + public struct JitManagerInfo { public TargetPointer ManagerAddress; @@ -34,6 +57,7 @@ public interface IExecutionManager : IContract TargetPointer GetDebugInfo(CodeBlockHandle codeInfoHandle, out bool hasFlagByte) => throw new NotImplementedException(); void GetGCInfo(CodeBlockHandle codeInfoHandle, out TargetPointer gcInfo, out uint gcVersion) => throw new NotImplementedException(); TargetNUInt GetRelativeOffset(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException(); + List GetExceptionClauses(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException(); JitManagerInfo GetEEJitManagerInfo() => throw new NotImplementedException(); } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs index 767effc40b595e..ed2bba9751bd34 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs @@ -33,6 +33,10 @@ public enum DataType EEAllocContext, Exception, ExceptionInfo, + EEExceptionClause, + ExceptionLookupTableEntry, + EEILException, + R2RExceptionClause, RuntimeThreadLocals, IdDispenser, Module, @@ -105,6 +109,9 @@ public enum DataType ILCodeVersionNode, ReadyToRunInfo, ReadyToRunHeader, + ReadyToRunSection, + ReadyToRunCoreHeader, + ReadyToRunCoreInfo, ImageDataDirectory, RuntimeFunction, HashMap, @@ -148,6 +155,7 @@ public enum DataType RCWCleanupList, RCW, CtxEntry, + InterfaceEntry, /* GC Data Types */ diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs index eaecb87a838900..b06840d4c35bd2 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs @@ -94,6 +94,7 @@ public static class Globals public const string TearOffAddRefSimple = nameof(TearOffAddRefSimple); public const string TearOffAddRefSimpleInner = nameof(TearOffAddRefSimpleInner); public const string RCWCleanupList = nameof(RCWCleanupList); + public const string RCWInterfaceCacheSize = nameof(RCWInterfaceCacheSize); public const string HashMapSlotsPerBucket = nameof(HashMapSlotsPerBucket); public const string HashMapValueMask = nameof(HashMapValueMask); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/BuiltInCOM_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/BuiltInCOM_1.cs index efecac106ae097..521a7e21421376 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/BuiltInCOM_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/BuiltInCOM_1.cs @@ -198,4 +198,23 @@ private TargetPointer GetSTAThread(Data.RCW rcw) Data.CtxEntry ctxEntry = _target.ProcessedData.GetOrAdd(ctxEntryPtr); return ctxEntry.STAThread; } + + public IEnumerable<(TargetPointer MethodTable, TargetPointer Unknown)> GetRCWInterfaces(TargetPointer rcw) + { + Data.RCW rcwData = _target.ProcessedData.GetOrAdd(rcw); + foreach (Data.InterfaceEntry entry in rcwData.InterfaceEntries) + { + if (entry.Unknown != TargetPointer.Null) + { + yield return (entry.MethodTable, entry.Unknown); + } + } + } + + public TargetPointer GetRCWContext(TargetPointer rcw) + { + Data.RCW rcwData = _target.ProcessedData.GetOrAdd(rcw); + + return rcwData.CtxCookie; + } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.EEJitManager.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.EEJitManager.cs index c8cb38eb9271bb..b275e10ab766fb 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.EEJitManager.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.EEJitManager.cs @@ -178,5 +178,25 @@ private bool GetRealCodeHeader(RangeSection rangeSection, TargetPointer codeStar realCodeHeader = Target.ProcessedData.GetOrAdd(codeHeaderAddress); return true; } + + public override void GetExceptionClauses(RangeSection rangeSection, CodeBlockHandle codeInfoHandle, out TargetPointer startAddr, out TargetPointer endAddr) + { + startAddr = TargetPointer.Null; + endAddr = TargetPointer.Null; + + if (rangeSection.Data == null) + throw new ArgumentException(nameof(rangeSection)); + + Data.RealCodeHeader? realCodeHeader; + if (!GetRealCodeHeader(rangeSection, codeInfoHandle.Address, out realCodeHeader) || realCodeHeader == null) + return; + + if (realCodeHeader.JitEHInfo == null) + return; + + TargetNUInt numEHInfos = Target.ReadNUInt(realCodeHeader.JitEHInfo.Address - (ulong)Target.PointerSize); + startAddr = realCodeHeader.JitEHInfo.Clauses; + endAddr = startAddr + numEHInfos.Value * Target.GetTypeInfo(DataType.EEExceptionClause).Size!.Value; + } } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs index 0ac6b4784946fe..ff08e588e2823e 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using Microsoft.Diagnostics.DataContractReader.Data; using Microsoft.Diagnostics.DataContractReader.ExecutionManagerHelpers; namespace Microsoft.Diagnostics.DataContractReader.Contracts; @@ -23,6 +24,11 @@ public ReadyToRunJitManager(Target target) : base(target) _runtimeFunctions = RuntimeFunctionLookup.Create(target); } + private enum ReadyToRunSectionType + { + ExceptionInfo = 104, + } + public override bool GetMethodInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, [NotNullWhen(true)] out CodeBlock? info) { // ReadyToRunJitManager::JitCodeToMethodInfo @@ -261,7 +267,84 @@ private TargetPointer GetMethodDescForRuntimeFunction(Data.ReadyToRunInfo r2rInf return methodDesc; } - #endregion + + private void GetExceptionClauses(TargetPointer exceptionLookupTableAddr, uint count, TargetPointer rangeStart, uint methodRVA, out TargetPointer startExInfoRVA, out TargetPointer endExInfoRVA) + { + startExInfoRVA = TargetPointer.Null; + endExInfoRVA = TargetPointer.Null; + if (count < 2) + return; + + uint low = 0; + uint high = count - 2; + uint entrySize = Target.GetTypeInfo(DataType.ExceptionLookupTableEntry).Size!.Value; + while (high - low > 10) + { + uint mid = low + ((high - low) / 2); + Data.ExceptionLookupTableEntry middleEntry = Target.ProcessedData.GetOrAdd(exceptionLookupTableAddr + (mid * entrySize)); + + if (methodRVA < middleEntry.MethodStartRVA) + { + high = mid - 1; + } + else + { + low = mid; + } + } + + for (uint i = low; i <= high; i++) + { + Data.ExceptionLookupTableEntry entry = Target.ProcessedData.GetOrAdd(exceptionLookupTableAddr + (i * entrySize)); + if (entry.MethodStartRVA == methodRVA) + { + Data.ExceptionLookupTableEntry nextEntry = Target.ProcessedData.GetOrAdd(exceptionLookupTableAddr + ((i + 1) * entrySize)); + startExInfoRVA = new TargetPointer(entry.ExceptionInfoRVA + rangeStart); + endExInfoRVA = new TargetPointer(nextEntry.ExceptionInfoRVA + rangeStart); + return; + } + } + + } + + public override void GetExceptionClauses(RangeSection range, CodeBlockHandle cbh, out TargetPointer startAddr, out TargetPointer endAddr) + { + Data.ReadyToRunInfo r2rInfo = GetReadyToRunInfo(range); + ImageDataDirectory? section = FindSection(r2rInfo, (uint)ReadyToRunSectionType.ExceptionInfo); + if (section == null) + { + startAddr = TargetPointer.Null; + endAddr = TargetPointer.Null; + return; + } + + uint count = section.Size / Target.GetTypeInfo(DataType.ExceptionLookupTableEntry).Size!.Value; + ulong exceptionLookupTableAddr = section.VirtualAddress + r2rInfo.LoadedImageBase; + + GetMethodRVAAndRangeStart(cbh, out TargetPointer methodStart, out TargetPointer rangeStart); + uint methodRVA = (uint)(methodStart - rangeStart); + + GetExceptionClauses(exceptionLookupTableAddr, count, rangeStart, methodRVA, out startAddr, out endAddr); + } + + private ImageDataDirectory? FindSection(Data.ReadyToRunInfo r2rInfo, uint sectionType) + { + Data.ReadyToRunCoreInfo coreInfo = Target.ProcessedData.GetOrAdd(r2rInfo.Composite); + foreach (Data.ReadyToRunSection section in coreInfo.Header.Sections) + { + if (section.Type == sectionType) + return section.Section; + } + return null; + } + + private void GetMethodRVAAndRangeStart(CodeBlockHandle cbh, out TargetPointer methodStart, out TargetPointer rangeStart) + { + IExecutionManager executionManager = Target.Contracts.ExecutionManager; + methodStart = executionManager.GetStartAddress(cbh).AsTargetPointer; + rangeStart = executionManager.GetUnwindInfoBaseAddress(cbh); + } + } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.cs index 00bd5b8751af6c..ad9d24248d3972 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using Microsoft.Diagnostics.DataContractReader.ExecutionManagerHelpers; +using Microsoft.Diagnostics.DataContractReader.Data; namespace Microsoft.Diagnostics.DataContractReader.Contracts; @@ -64,6 +65,14 @@ private enum JitTypes TYPE_INTERPRETER = 3 }; + private enum ExceptionClauseFlags_1 : uint + { + Filter = 0x1, + Finally = 0x2, + Fault = 0x4, + CachedClass = 0x10000000, + } + private abstract class JitManager { public Target Target { get; } @@ -83,6 +92,7 @@ public abstract void GetMethodRegionInfo( public abstract TargetPointer GetUnwindInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress); public abstract TargetPointer GetDebugInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, out bool hasFlagByte); public abstract void GetGCInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, out TargetPointer gcInfo, out uint gcVersion); + public abstract void GetExceptionClauses(RangeSection rangeSection, CodeBlockHandle codeInfoHandle, out TargetPointer startAddr, out TargetPointer endAddr); } private sealed class RangeSection @@ -367,4 +377,104 @@ private RangeSection RangeSectionFromCodeBlockHandle(CodeBlockHandle codeInfoHan RangeSection range = RangeSection.Find(_target, _topRangeSectionMap, _rangeSectionMapLookup, codeInfoHandle.Address.Value); return range; } + + private static ExceptionClauseInfo.ExceptionClauseFlags GetExceptionClauseFlags(uint flags) + { + if ((flags & (uint)ExceptionClauseFlags_1.Fault) != 0) return ExceptionClauseInfo.ExceptionClauseFlags.Fault; + if ((flags & (uint)ExceptionClauseFlags_1.Finally) != 0) return ExceptionClauseInfo.ExceptionClauseFlags.Finally; + if ((flags & (uint)ExceptionClauseFlags_1.Filter) != 0) return ExceptionClauseInfo.ExceptionClauseFlags.Filter; + return ExceptionClauseInfo.ExceptionClauseFlags.Typed; + } + + private static bool IsFilterHandler(ExceptionClauseInfo.ExceptionClauseFlags flags) => flags == ExceptionClauseInfo.ExceptionClauseFlags.Filter; + private static bool IsTypedHandler(ExceptionClauseInfo.ExceptionClauseFlags flags) => flags == ExceptionClauseInfo.ExceptionClauseFlags.Typed; + private static bool HasCachedTypeHandle(IExceptionClauseData clause) => (clause.Flags & (uint)ExceptionClauseFlags_1.CachedClass) != 0; + + private bool IsObjectType(TargetPointer moduleAddr, uint classToken) + { + ILoader loader = _target.Contracts.Loader; + ModuleHandle module = loader.GetModuleHandleFromModulePtr(moduleAddr); + ModuleLookupTables tables = loader.GetLookupTables(module); + + TargetPointer resolvedMethodTable = (EcmaMetadataUtils.TokenType)(classToken & EcmaMetadataUtils.TokenTypeMask) switch + { + EcmaMetadataUtils.TokenType.mdtTypeDef => loader.GetModuleLookupMapElement(tables.TypeDefToMethodTable, classToken, out _), + EcmaMetadataUtils.TokenType.mdtTypeRef => loader.GetModuleLookupMapElement(tables.TypeRefToMethodTable, classToken, out _), + _ => TargetPointer.Null, + }; + + if (resolvedMethodTable == TargetPointer.Null) + return false; + + TargetPointer objectMethodTable = _target.ReadPointer( + _target.ReadGlobalPointer(Constants.Globals.ObjectMethodTable)); + + return resolvedMethodTable == objectMethodTable; + } + + List IExecutionManager.GetExceptionClauses(CodeBlockHandle codeInfoHandle) + { + RangeSection range = RangeSectionFromCodeBlockHandle(codeInfoHandle); + if (range.Data == null) + return new List(); + + JitManager jitManager = GetJitManager(range.Data); + jitManager.GetExceptionClauses(range, codeInfoHandle, out TargetPointer startAddr, out TargetPointer endAddr); + bool isR2R = jitManager is ReadyToRunJitManager; + DataType clauseType = isR2R ? DataType.R2RExceptionClause : DataType.EEExceptionClause; + uint clauseSize = _target.GetTypeInfo(clauseType).Size!.Value; + TargetPointer methodDescPtr = ((IExecutionManager)this).GetMethodDesc(codeInfoHandle); + IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + MethodDescHandle mdHandle = rts.GetMethodDescHandle(methodDescPtr); + TargetPointer mtPtr = rts.GetMethodTable(mdHandle); + TypeHandle th = rts.GetTypeHandle(mtPtr); + TargetPointer handleModuleAddr = rts.GetModule(th); + + List exceptionClauses = new List(); + for (TargetPointer addr = startAddr; addr < endAddr; addr += clauseSize) + { + IExceptionClauseData entry = isR2R + ? _target.ProcessedData.GetOrAdd(addr) + : _target.ProcessedData.GetOrAdd(addr); + + ExceptionClauseInfo.ExceptionClauseFlags flags = GetExceptionClauseFlags(entry.Flags); + uint? filterOffset = IsFilterHandler(flags) ? entry.FilterOffset : null; + TargetNUInt? typeHandle = null; + bool? isCatchAllHandler = null; + TargetPointer? moduleAddr = null; + uint? classToken = null; + + if (IsTypedHandler(flags)) + { + if (HasCachedTypeHandle(entry) && !isR2R) // Dynamic method path: we only have a cached type handle, no token. + { + typeHandle = ((EEExceptionClause)entry).TypeHandle; + TargetPointer objectMethodTable = _target.ReadPointer( + _target.ReadGlobalPointer(Constants.Globals.ObjectMethodTable)); + isCatchAllHandler = typeHandle.Value.Value == objectMethodTable.Value; + } + else + { + isCatchAllHandler = IsObjectType(handleModuleAddr, entry.ClassToken); + moduleAddr = handleModuleAddr; + classToken = entry.ClassToken; + } + } + + exceptionClauses.Add(new ExceptionClauseInfo + { + ClauseType = flags, + IsCatchAllHandler = isCatchAllHandler, + TryStartPC = entry.TryStartPC, + TryEndPC = entry.TryEndPC, + HandlerStartPC = entry.HandlerStartPC, + HandlerEndPC = entry.HandlerEndPC, + FilterOffset = filterOffset, + ClassToken = classToken, + TypeHandle = typeHandle, + ModuleAddr = moduleAddr, + }); + } + return exceptionClauses; + } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_1.cs index f775821e3dc206..76faf0d050c02a 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_1.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Generic; using Microsoft.Diagnostics.DataContractReader.ExecutionManagerHelpers; namespace Microsoft.Diagnostics.DataContractReader.Contracts; @@ -26,5 +27,6 @@ internal ExecutionManager_1(Target target, Data.RangeSectionMap topRangeSectionM public TargetPointer GetDebugInfo(CodeBlockHandle codeInfoHandle, out bool hasFlagByte) => _executionManagerCore.GetDebugInfo(codeInfoHandle, out hasFlagByte); public void GetGCInfo(CodeBlockHandle codeInfoHandle, out TargetPointer gcInfo, out uint gcVersion) => _executionManagerCore.GetGCInfo(codeInfoHandle, out gcInfo, out gcVersion); public TargetNUInt GetRelativeOffset(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetRelativeOffset(codeInfoHandle); + public List GetExceptionClauses(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetExceptionClauses(codeInfoHandle); public JitManagerInfo GetEEJitManagerInfo() => _executionManagerCore.GetEEJitManagerInfo(); } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_2.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_2.cs index 9193a8a3f94c31..8e7f6bb5267510 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_2.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_2.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Generic; using Microsoft.Diagnostics.DataContractReader.ExecutionManagerHelpers; namespace Microsoft.Diagnostics.DataContractReader.Contracts; @@ -26,5 +27,6 @@ internal ExecutionManager_2(Target target, Data.RangeSectionMap topRangeSectionM public TargetPointer GetDebugInfo(CodeBlockHandle codeInfoHandle, out bool hasFlagByte) => _executionManagerCore.GetDebugInfo(codeInfoHandle, out hasFlagByte); public void GetGCInfo(CodeBlockHandle codeInfoHandle, out TargetPointer gcInfo, out uint gcVersion) => _executionManagerCore.GetGCInfo(codeInfoHandle, out gcInfo, out gcVersion); public TargetNUInt GetRelativeOffset(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetRelativeOffset(codeInfoHandle); + public List GetExceptionClauses(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetExceptionClauses(codeInfoHandle); public JitManagerInfo GetEEJitManagerInfo() => _executionManagerCore.GetEEJitManagerInfo(); } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/Helpers/UnwindDataSize.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/Helpers/UnwindDataSize.cs index 26f53e11ca205a..952087ab9be95d 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/Helpers/UnwindDataSize.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/Helpers/UnwindDataSize.cs @@ -72,14 +72,15 @@ public static uint GetUnwindDataSize(Target target, TargetPointer unwindInfo, Ru uint unwindWords; uint epilogScopes; - // If both Epilog Count and Code Word is not zero - // Info of Epilog and Unwind scopes are given by 1 word header - // Otherwise this info is given by a 2 word header - if ((xdata0 >> 27) != 0) + unwindWords = (xdata0 >> 27) & 0x1f; + epilogScopes = (xdata0 >> 22) & 0x1f; + + // If either Epilog Count or Code Word is non-zero, + // epilog/unwind scope info is in the 1-word header. + // Otherwise this info is given by a 2-word header. + if (unwindWords != 0 || epilogScopes != 0) { size = 4; - epilogScopes = (xdata0 >> 22) & 0x1f; - unwindWords = (xdata0 >> 27) & 0x1f; } else { diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/GCInfoFactory.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/GCInfoFactory.cs index 47135e41ab4861..50f1a36e7c70ee 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/GCInfoFactory.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/GCInfoFactory.cs @@ -16,6 +16,8 @@ IGCInfo IContractFactory.CreateContract(Target target, int version) (1, RuntimeInfoArchitecture.X64) => new GCInfo_1(target), (1, RuntimeInfoArchitecture.Arm64) => new GCInfo_1(target), (1, RuntimeInfoArchitecture.Arm) => new GCInfo_1(target), + (1, RuntimeInfoArchitecture.LoongArch64) => new GCInfo_1(target), + (1, RuntimeInfoArchitecture.RiscV64) => new GCInfo_1(target), _ => default(GCInfo), }; } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/PlatformTraits/LoongArch64GCInfoTraits.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/PlatformTraits/LoongArch64GCInfoTraits.cs new file mode 100644 index 00000000000000..39e0f13bfcc90d --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/PlatformTraits/LoongArch64GCInfoTraits.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Microsoft.Diagnostics.DataContractReader.Contracts.GCInfoHelpers; + +internal class LoongArch64GCInfoTraits : IGCInfoTraits +{ + public static uint DenormalizeStackBaseRegister(uint reg) => reg == 0 ? 22u : 3u; + public static uint DenormalizeCodeLength(uint len) => len << 2; + public static uint NormalizeCodeLength(uint len) => len >> 2; + public static uint DenormalizeCodeOffset(uint offset) => offset << 2; + public static uint NormalizeCodeOffset(uint offset) => offset >> 2; + public static int DenormalizeStackSlot(int x) => x << 3; + public static uint DenormalizeSizeOfStackArea(uint size) => size << 3; + + public static int GENERICS_INST_CONTEXT_STACK_SLOT_ENCBASE => 6; + + public static int GS_COOKIE_STACK_SLOT_ENCBASE => 6; + public static int CODE_LENGTH_ENCBASE => 8; + + public static int STACK_BASE_REGISTER_ENCBASE => 2; + public static int SIZE_OF_STACK_AREA_ENCBASE => 3; + public static int SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA_ENCBASE => 4; + public static int SIZE_OF_EDIT_AND_CONTINUE_FIXED_STACK_FRAME_ENCBASE => 0; + public static int REVERSE_PINVOKE_FRAME_ENCBASE => 6; + public static int NUM_REGISTERS_ENCBASE => 3; + public static int NUM_STACK_SLOTS_ENCBASE => 2; + public static int NUM_UNTRACKED_SLOTS_ENCBASE => 1; + public static int NORM_PROLOG_SIZE_ENCBASE => 5; + public static int NORM_EPILOG_SIZE_ENCBASE => 3; + public static int INTERRUPTIBLE_RANGE_DELTA1_ENCBASE => 6; + public static int INTERRUPTIBLE_RANGE_DELTA2_ENCBASE => 6; + public static int REGISTER_ENCBASE => 3; + public static int REGISTER_DELTA_ENCBASE => 2; + public static int STACK_SLOT_ENCBASE => 6; + public static int STACK_SLOT_DELTA_ENCBASE => 4; + public static int NUM_SAFE_POINTS_ENCBASE => 3; + public static int NUM_INTERRUPTIBLE_RANGES_ENCBASE => 1; + + public static bool HAS_FIXED_STACK_PARAMETER_SCRATCH_AREA => true; +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/PlatformTraits/RISCV64GCInfoTraits.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/PlatformTraits/RISCV64GCInfoTraits.cs new file mode 100644 index 00000000000000..ab64543814230e --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCInfo/PlatformTraits/RISCV64GCInfoTraits.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Microsoft.Diagnostics.DataContractReader.Contracts.GCInfoHelpers; + +internal class RISCV64GCInfoTraits : IGCInfoTraits +{ + public static uint DenormalizeStackBaseRegister(uint reg) => reg == 0 ? 8u : 2u; + public static uint DenormalizeCodeLength(uint len) => len << 1; + public static uint NormalizeCodeLength(uint len) => len >> 1; + public static uint DenormalizeCodeOffset(uint offset) => offset << 1; + public static uint NormalizeCodeOffset(uint offset) => offset >> 1; + public static int DenormalizeStackSlot(int x) => x << 3; + public static uint DenormalizeSizeOfStackArea(uint size) => size << 3; + + public static int GENERICS_INST_CONTEXT_STACK_SLOT_ENCBASE => 6; + + public static int GS_COOKIE_STACK_SLOT_ENCBASE => 6; + public static int CODE_LENGTH_ENCBASE => 8; + + public static int STACK_BASE_REGISTER_ENCBASE => 2; + public static int SIZE_OF_STACK_AREA_ENCBASE => 3; + public static int SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA_ENCBASE => 4; + public static int SIZE_OF_EDIT_AND_CONTINUE_FIXED_STACK_FRAME_ENCBASE => 4; + public static int REVERSE_PINVOKE_FRAME_ENCBASE => 6; + public static int NUM_REGISTERS_ENCBASE => 3; + public static int NUM_STACK_SLOTS_ENCBASE => 2; + public static int NUM_UNTRACKED_SLOTS_ENCBASE => 1; + public static int NORM_PROLOG_SIZE_ENCBASE => 5; + public static int NORM_EPILOG_SIZE_ENCBASE => 3; + public static int INTERRUPTIBLE_RANGE_DELTA1_ENCBASE => 6; + public static int INTERRUPTIBLE_RANGE_DELTA2_ENCBASE => 6; + public static int REGISTER_ENCBASE => 3; + public static int REGISTER_DELTA_ENCBASE => 2; + public static int STACK_SLOT_ENCBASE => 6; + public static int STACK_SLOT_DELTA_ENCBASE => 4; + public static int NUM_SAFE_POINTS_ENCBASE => 3; + public static int NUM_INTERRUPTIBLE_RANGES_ENCBASE => 1; + + public static bool HAS_FIXED_STACK_PARAMETER_SCRATCH_AREA => true; +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Loader_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Loader_1.cs index 6f789c36dc3986..bf31a0128950d1 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Loader_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Loader_1.cs @@ -12,6 +12,7 @@ namespace Microsoft.Diagnostics.DataContractReader.Contracts; internal readonly struct Loader_1 : ILoader { + private const string DefaultDomainFriendlyName = "DefaultDomain"; private const uint ASSEMBLY_NOTIFYFLAGS_PROFILER_NOTIFIED = 0x1; // Assembly Notify Flag for profiler notification private enum ModuleFlags_1 : uint @@ -135,7 +136,7 @@ string ILoader.GetAppDomainFriendlyName() Data.AppDomain appDomain = _target.ProcessedData.GetOrAdd(_target.ReadPointer(appDomainPointer)); return appDomain.FriendlyName != TargetPointer.Null ? _target.ReadUtf16String(appDomain.FriendlyName) - : string.Empty; + : DefaultDomainFriendlyName; } TargetPointer ILoader.GetModule(ModuleHandle handle) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/IPlatformAgnosticContext.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/IPlatformAgnosticContext.cs index 5783028f84cf55..496a70cebf0aff 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/IPlatformAgnosticContext.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/IPlatformAgnosticContext.cs @@ -32,6 +32,7 @@ public static IPlatformAgnosticContext GetContextForPlatform(Target target) RuntimeInfoArchitecture.X64 => new ContextHolder(), RuntimeInfoArchitecture.Arm => new ContextHolder(), RuntimeInfoArchitecture.Arm64 => new ContextHolder(), + RuntimeInfoArchitecture.LoongArch64 => new ContextHolder(), RuntimeInfoArchitecture.RiscV64 => new ContextHolder(), RuntimeInfoArchitecture.Unknown => throw new InvalidOperationException($"Processor architecture is required for creating a platform specific context and is not provided by the target"), _ => throw new InvalidOperationException($"Unsupported architecture {runtimeInfo.GetTargetArchitecture()}"), diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/LoongArch64/LoongArch64Unwinder.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/LoongArch64/LoongArch64Unwinder.cs index 611fa0c6689e8c..84462de51096b0 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/LoongArch64/LoongArch64Unwinder.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/LoongArch64/LoongArch64Unwinder.cs @@ -281,7 +281,7 @@ private bool VirtualUnwindFull( unwindCodePtr += 1; bool isEndCode = OpcodeIsEnd(curCode); - if (!ProcessUnwindCode(ref context, curCode, ref unwindCodePtr, unwindCodesEndPtr, ref accumulatedSaveNexts)) + if (!ProcessUnwindCode(ref context, curCode, ref unwindCodePtr, unwindCodesEndPtr, ref accumulatedSaveNexts, ref finalPcFromRa)) { return false; } @@ -332,7 +332,7 @@ private static bool OpcodeIsEnd(byte opcode) return (opcode & 0xfe) == 0xe4; } - private bool ProcessUnwindCode(ref LoongArch64Context context, byte curCode, ref TargetPointer unwindCodePtr, TargetPointer unwindCodesEndPtr, ref uint accumulatedSaveNexts) + private bool ProcessUnwindCode(ref LoongArch64Context context, byte curCode, ref TargetPointer unwindCodePtr, TargetPointer unwindCodesEndPtr, ref uint accumulatedSaveNexts, ref bool finalPcFromRa) { try { @@ -518,7 +518,7 @@ private bool ProcessUnwindCode(ref LoongArch64Context context, byte curCode, ref // custom_0 (111010xx): restore custom structure // - else if (curCode >= 0xe8 && curCode <= 0xeb) + else if (curCode >= 0xe8 && curCode <= 0xec) { if (accumulatedSaveNexts != 0) { @@ -526,7 +526,12 @@ private bool ProcessUnwindCode(ref LoongArch64Context context, byte curCode, ref return false; } - return false; + if (!UnwindCustom(ref context, curCode)) + { + return false; + } + + finalPcFromRa = false; } // @@ -547,6 +552,160 @@ private bool ProcessUnwindCode(ref LoongArch64Context context, byte curCode, ref } } + private unsafe bool UnwindCustom( + ref LoongArch64Context context, + byte customCode) + { + ulong startingSp = context.Sp; + + switch (customCode) + { + // + // Trap frame case — not applicable for LoongArch64 (no kernel trap frame defined) + // + case 0xE8: // MSFT_OP_TRAP_FRAME: + return false; + + // + // Machine frame case + // + case 0xE9: // MSFT_OP_MACHINE_FRAME: + { + // + // Restore the SP and PC, and clear the unwound-to-call flag + // + context.Sp = _target.Read(startingSp + 0); + context.Pc = _target.Read(startingSp + 8); + context.ContextFlags &= (uint)~ContextFlagsValues.CONTEXT_UNWOUND_TO_CALL; + break; + } + + // + // Context case + // + case 0xEA: // MSFT_OP_CONTEXT: + { + // + // Restore all general-purpose registers from the CONTEXT on the stack + // + TargetPointer sourceAddress = startingSp + (uint)Marshal.OffsetOf(nameof(LoongArch64Context.R0)); + context.R0 = _target.Read(sourceAddress); + + sourceAddress = startingSp + (uint)Marshal.OffsetOf(nameof(LoongArch64Context.Ra)); + context.Ra = _target.Read(sourceAddress); + + sourceAddress = startingSp + (uint)Marshal.OffsetOf(nameof(LoongArch64Context.Tp)); + context.Tp = _target.Read(sourceAddress); + + sourceAddress = startingSp + (uint)Marshal.OffsetOf(nameof(LoongArch64Context.Sp)); + context.Sp = _target.Read(sourceAddress); + + sourceAddress = startingSp + (uint)Marshal.OffsetOf(nameof(LoongArch64Context.A0)); + context.A0 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.A1 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.A2 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.A3 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.A4 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.A5 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.A6 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.A7 = _target.Read(sourceAddress); + + sourceAddress = startingSp + (uint)Marshal.OffsetOf(nameof(LoongArch64Context.T0)); + context.T0 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.T1 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.T2 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.T3 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.T4 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.T5 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.T6 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.T7 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.T8 = _target.Read(sourceAddress); + + sourceAddress = startingSp + (uint)Marshal.OffsetOf(nameof(LoongArch64Context.X0)); + context.X0 = _target.Read(sourceAddress); + + sourceAddress = startingSp + (uint)Marshal.OffsetOf(nameof(LoongArch64Context.Fp)); + context.Fp = _target.Read(sourceAddress); + + sourceAddress = startingSp + (uint)Marshal.OffsetOf(nameof(LoongArch64Context.S0)); + context.S0 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.S1 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.S2 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.S3 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.S4 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.S5 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.S6 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.S7 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.S8 = _target.Read(sourceAddress); + + // + // Restore PC and floating-point registers + // + sourceAddress = startingSp + (uint)Marshal.OffsetOf(nameof(LoongArch64Context.Pc)); + context.Pc = _target.Read(sourceAddress); + + sourceAddress = startingSp + (uint)Marshal.OffsetOf(nameof(LoongArch64Context.F)); + for (uint regIndex = 0; regIndex < 32; regIndex++) + { + context.F[regIndex] = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + } + + sourceAddress = startingSp + (uint)Marshal.OffsetOf(nameof(LoongArch64Context.Fcc)); + context.Fcc = _target.Read(sourceAddress); + + sourceAddress = startingSp + (uint)Marshal.OffsetOf(nameof(LoongArch64Context.Fcsr)); + context.Fcsr = _target.Read(sourceAddress); + + // + // Inherit the unwound-to-call flag from this context + // + sourceAddress = startingSp + (uint)Marshal.OffsetOf(nameof(LoongArch64Context.ContextFlags)); + context.ContextFlags &= (uint)~ContextFlagsValues.CONTEXT_UNWOUND_TO_CALL; + context.ContextFlags |= + _target.Read(sourceAddress) & (uint)ContextFlagsValues.CONTEXT_UNWOUND_TO_CALL; + break; + } + + case 0xEB: // MSFT_OP_EC_CONTEXT: + // Not applicable for LoongArch64 + return false; + + case 0xEC: // MSFT_OP_CLEAR_UNWOUND_TO_CALL + context.ContextFlags &= (uint)~ContextFlagsValues.CONTEXT_UNWOUND_TO_CALL; + context.Pc = context.Ra; + break; + + default: + return false; + } + + return true; + } + private void SetRegisterFromOffset(ref LoongArch64Context context, uint regNum, ulong address) { try diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/RISCV64/RISCV64Unwinder.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/RISCV64/RISCV64Unwinder.cs index f6044d0037b805..c8f8fd4cda5f9d 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/RISCV64/RISCV64Unwinder.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/RISCV64/RISCV64Unwinder.cs @@ -295,7 +295,7 @@ private bool VirtualUnwindFull( unwindCodePtr += 1; bool isEndCode = OpcodeIsEnd(curCode); - if (!ProcessUnwindCode(ref context, curCode, ref unwindCodePtr, unwindCodesEndPtr, ref accumulatedSaveNexts)) + if (!ProcessUnwindCode(ref context, curCode, ref unwindCodePtr, unwindCodesEndPtr, ref accumulatedSaveNexts, ref finalPcFromRa)) { return false; } @@ -346,7 +346,7 @@ private static bool OpcodeIsEnd(byte opcode) return (opcode & 0xfe) == 0xe4; } - private bool ProcessUnwindCode(ref RISCV64Context context, byte curCode, ref TargetPointer unwindCodePtr, TargetPointer unwindCodesEndPtr, ref uint accumulatedSaveNexts) + private bool ProcessUnwindCode(ref RISCV64Context context, byte curCode, ref TargetPointer unwindCodePtr, TargetPointer unwindCodesEndPtr, ref uint accumulatedSaveNexts, ref bool finalPcFromRa) { try { @@ -528,6 +528,26 @@ private bool ProcessUnwindCode(ref RISCV64Context context, byte curCode, ref Tar { } + // + // custom_0 (111010xx): restore custom structure + // + + else if (curCode >= 0xe8 && curCode <= 0xec) + { + if (accumulatedSaveNexts != 0) + { + // invalid sequence + return false; + } + + if (!UnwindCustom(ref context, curCode)) + { + return false; + } + + finalPcFromRa = false; + } + // // Anything else is invalid // @@ -546,6 +566,156 @@ private bool ProcessUnwindCode(ref RISCV64Context context, byte curCode, ref Tar } } + private unsafe bool UnwindCustom( + ref RISCV64Context context, + byte customCode) + { + ulong startingSp = context.Sp; + + switch (customCode) + { + // + // Trap frame case — not applicable for RISCV64 (no kernel trap frame defined) + // + case 0xE8: // MSFT_OP_TRAP_FRAME: + return false; + + // + // Machine frame case + // + case 0xE9: // MSFT_OP_MACHINE_FRAME: + { + // + // Restore the SP and PC, and clear the unwound-to-call flag + // + context.Sp = _target.Read(startingSp + 0); + context.Pc = _target.Read(startingSp + 8); + context.ContextFlags &= (uint)~ContextFlagsValues.CONTEXT_UNWOUND_TO_CALL; + break; + } + + // + // Context case + // + case 0xEA: // MSFT_OP_CONTEXT: + { + // + // Restore all general-purpose registers from the CONTEXT on the stack + // + TargetPointer sourceAddress = startingSp + (uint)Marshal.OffsetOf(nameof(RISCV64Context.Ra)); + context.Ra = _target.Read(sourceAddress); + + sourceAddress = startingSp + (uint)Marshal.OffsetOf(nameof(RISCV64Context.Sp)); + context.Sp = _target.Read(sourceAddress); + + sourceAddress = startingSp + (uint)Marshal.OffsetOf(nameof(RISCV64Context.Gp)); + context.Gp = _target.Read(sourceAddress); + + sourceAddress = startingSp + (uint)Marshal.OffsetOf(nameof(RISCV64Context.Tp)); + context.Tp = _target.Read(sourceAddress); + + sourceAddress = startingSp + (uint)Marshal.OffsetOf(nameof(RISCV64Context.T0)); + context.T0 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.T1 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.T2 = _target.Read(sourceAddress); + + sourceAddress = startingSp + (uint)Marshal.OffsetOf(nameof(RISCV64Context.Fp)); + context.Fp = _target.Read(sourceAddress); + + sourceAddress = startingSp + (uint)Marshal.OffsetOf(nameof(RISCV64Context.S1)); + context.S1 = _target.Read(sourceAddress); + + sourceAddress = startingSp + (uint)Marshal.OffsetOf(nameof(RISCV64Context.A0)); + context.A0 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.A1 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.A2 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.A3 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.A4 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.A5 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.A6 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.A7 = _target.Read(sourceAddress); + + sourceAddress = startingSp + (uint)Marshal.OffsetOf(nameof(RISCV64Context.S2)); + context.S2 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.S3 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.S4 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.S5 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.S6 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.S7 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.S8 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.S9 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.S10 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.S11 = _target.Read(sourceAddress); + + sourceAddress = startingSp + (uint)Marshal.OffsetOf(nameof(RISCV64Context.T3)); + context.T3 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.T4 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.T5 = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + context.T6 = _target.Read(sourceAddress); + + // + // Restore SP, RA, PC, and floating-point status + // + sourceAddress = startingSp + (uint)Marshal.OffsetOf(nameof(RISCV64Context.Pc)); + context.Pc = _target.Read(sourceAddress); + + sourceAddress = startingSp + (uint)Marshal.OffsetOf(nameof(RISCV64Context.F)); + for (uint regIndex = 0; regIndex < 32; regIndex++) + { + context.F[regIndex] = _target.Read(sourceAddress); + sourceAddress += sizeof(ulong); + } + + sourceAddress = startingSp + (uint)Marshal.OffsetOf(nameof(RISCV64Context.Fcsr)); + context.Fcsr = _target.Read(sourceAddress); + + // + // Inherit the unwound-to-call flag from this context + // + sourceAddress = startingSp + (uint)Marshal.OffsetOf(nameof(RISCV64Context.ContextFlags)); + context.ContextFlags &= (uint)~ContextFlagsValues.CONTEXT_UNWOUND_TO_CALL; + context.ContextFlags |= + _target.Read(sourceAddress) & (uint)ContextFlagsValues.CONTEXT_UNWOUND_TO_CALL; + break; + } + + case 0xEB: // MSFT_OP_EC_CONTEXT: + // Not applicable for RISCV64 + return false; + + case 0xEC: // MSFT_OP_CLEAR_UNWOUND_TO_CALL + context.ContextFlags &= (uint)~ContextFlagsValues.CONTEXT_UNWOUND_TO_CALL; + context.Pc = context.Ra; + break; + + default: + return false; + } + + return true; + } + private void SetRegisterFromOffset(ref RISCV64Context context, uint regNum, ulong address) { try diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/RISCV64FrameHandler.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/RISCV64FrameHandler.cs index aac0c3e613b4a5..dea3b88ec3f042 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/RISCV64FrameHandler.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/RISCV64FrameHandler.cs @@ -27,4 +27,13 @@ public void HandleHijackFrame(HijackFrame frame) UpdateFromRegisterDict(args.Registers); } + + public override void HandleFaultingExceptionFrame(FaultingExceptionFrame frame) + { + base.HandleFaultingExceptionFrame(frame); + + // Clear the CONTEXT_UNWOUND_TO_CALL flag + const uint CONTEXT_UNWOUND_TO_CALL = (uint)ContextFlagsValues.CONTEXT_UNWOUND_TO_CALL; + _holder.Context.ContextFlags &= ~CONTEXT_UNWOUND_TO_CALL; + } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/EEILException.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/EEILException.cs new file mode 100644 index 00000000000000..0d3ee5f0abd681 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/EEILException.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +internal sealed class EEILException : IData +{ + static EEILException IData.Create(Target target, TargetPointer address) => new EEILException(target, address); + public EEILException(Target target, TargetPointer address) + { + Target.TypeInfo type = target.GetTypeInfo(DataType.EEILException); + Address = address; + Clauses = address + (ulong)type.Fields[nameof(Clauses)].Offset; + } + + public TargetPointer Address { get; init; } + public TargetPointer Clauses { get; init; } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ExceptionClause.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ExceptionClause.cs new file mode 100644 index 00000000000000..d8498f67440b59 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ExceptionClause.cs @@ -0,0 +1,67 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +internal interface IExceptionClauseData +{ + uint Flags { get; } + uint TryStartPC { get; } + uint TryEndPC { get; } + uint HandlerStartPC { get; } + uint HandlerEndPC { get; } + uint ClassToken { get; } + uint FilterOffset { get; } +} + +internal sealed class EEExceptionClause : IData, IExceptionClauseData +{ + static EEExceptionClause IData.Create(Target target, TargetPointer address) => new EEExceptionClause(target, address); + public EEExceptionClause(Target target, TargetPointer address) + { + Target.TypeInfo type = target.GetTypeInfo(DataType.EEExceptionClause); + + Flags = target.Read(address + (ulong)type.Fields[nameof(Flags)].Offset); + TryStartPC = target.Read(address + (ulong)type.Fields[nameof(TryStartPC)].Offset); + TryEndPC = target.Read(address + (ulong)type.Fields[nameof(TryEndPC)].Offset); + HandlerStartPC = target.Read(address + (ulong)type.Fields[nameof(HandlerStartPC)].Offset); + HandlerEndPC = target.Read(address + (ulong)type.Fields[nameof(HandlerEndPC)].Offset); + TypeHandle = target.ReadNUInt(address + (ulong)type.Fields[nameof(TypeHandle)].Offset); + ClassToken = target.Read(address + (ulong)type.Fields[nameof(TypeHandle)].Offset); + FilterOffset = ClassToken; + } + + public uint Flags { get; init; } + public uint TryStartPC { get; init; } + public uint TryEndPC { get; init; } + public uint HandlerStartPC { get; init; } + public uint HandlerEndPC { get; init; } + public TargetNUInt TypeHandle { get; init; } + public uint ClassToken { get; init; } + public uint FilterOffset { get; init; } +} + +internal sealed class R2RExceptionClause : IData, IExceptionClauseData +{ + static R2RExceptionClause IData.Create(Target target, TargetPointer address) => new R2RExceptionClause(target, address); + public R2RExceptionClause(Target target, TargetPointer address) + { + Target.TypeInfo type = target.GetTypeInfo(DataType.R2RExceptionClause); + + Flags = target.Read(address + (ulong)type.Fields[nameof(Flags)].Offset); + TryStartPC = target.Read(address + (ulong)type.Fields[nameof(TryStartPC)].Offset); + TryEndPC = target.Read(address + (ulong)type.Fields[nameof(TryEndPC)].Offset); + HandlerStartPC = target.Read(address + (ulong)type.Fields[nameof(HandlerStartPC)].Offset); + HandlerEndPC = target.Read(address + (ulong)type.Fields[nameof(HandlerEndPC)].Offset); + ClassToken = target.Read(address + (ulong)type.Fields[nameof(ClassToken)].Offset); + FilterOffset = ClassToken; + } + + public uint Flags { get; init; } + public uint TryStartPC { get; init; } + public uint TryEndPC { get; init; } + public uint HandlerStartPC { get; init; } + public uint HandlerEndPC { get; init; } + public uint ClassToken { get; init; } + public uint FilterOffset { get; init; } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ExceptionLookupTableEntry.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ExceptionLookupTableEntry.cs new file mode 100644 index 00000000000000..2b3d9cbe19822f --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ExceptionLookupTableEntry.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +internal sealed class ExceptionLookupTableEntry : IData +{ + static ExceptionLookupTableEntry IData.Create(Target target, TargetPointer address) => new ExceptionLookupTableEntry(target, address); + public ExceptionLookupTableEntry(Target target, TargetPointer address) + { + Target.TypeInfo type = target.GetTypeInfo(DataType.ExceptionLookupTableEntry); + MethodStartRVA = target.Read(address + (ulong)type.Fields[nameof(MethodStartRVA)].Offset); + ExceptionInfoRVA = target.Read(address + (ulong)type.Fields[nameof(ExceptionInfoRVA)].Offset); + } + + public uint MethodStartRVA { get; init; } + public uint ExceptionInfoRVA { get; init; } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/InterfaceEntry.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/InterfaceEntry.cs new file mode 100644 index 00000000000000..2beb7fdc8d903d --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/InterfaceEntry.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +internal sealed class InterfaceEntry : IData +{ + static InterfaceEntry IData.Create(Target target, TargetPointer address) => new InterfaceEntry(target, address); + public InterfaceEntry(Target target, TargetPointer address) + { + Target.TypeInfo type = target.GetTypeInfo(DataType.InterfaceEntry); + + MethodTable = target.ReadPointer(address + (ulong)type.Fields[nameof(MethodTable)].Offset); + Unknown = target.ReadPointer(address + (ulong)type.Fields[nameof(Unknown)].Offset); + } + + public TargetPointer MethodTable { get; init; } + public TargetPointer Unknown { get; init; } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/RCW.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/RCW.cs index d325653df89633..422a899090c23b 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/RCW.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/RCW.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Generic; + namespace Microsoft.Diagnostics.DataContractReader.Data; internal sealed class RCW : IData @@ -15,6 +17,17 @@ public RCW(Target target, TargetPointer address) Flags = target.Read(address + (ulong)type.Fields[nameof(Flags)].Offset); CtxCookie = target.ReadPointer(address + (ulong)type.Fields[nameof(CtxCookie)].Offset); CtxEntry = target.ReadPointer(address + (ulong)type.Fields[nameof(CtxEntry)].Offset); + TargetPointer interfaceEntriesAddr = address + (ulong)type.Fields[nameof(InterfaceEntries)].Offset; + + uint cacheSize = target.ReadGlobal(Constants.Globals.RCWInterfaceCacheSize); + Target.TypeInfo entryTypeInfo = target.GetTypeInfo(DataType.InterfaceEntry); + uint entrySize = entryTypeInfo.Size!.Value; + + for (uint i = 0; i < cacheSize; i++) + { + TargetPointer entryAddress = interfaceEntriesAddr + i * entrySize; + InterfaceEntries.Add(target.ProcessedData.GetOrAdd(entryAddress)); + } } public TargetPointer NextCleanupBucket { get; init; } @@ -22,4 +35,5 @@ public RCW(Target target, TargetPointer address) public uint Flags { get; init; } public TargetPointer CtxCookie { get; init; } public TargetPointer CtxEntry { get; init; } + public List InterfaceEntries { get; } = new List(); } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ReadyToRunCoreHeader.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ReadyToRunCoreHeader.cs new file mode 100644 index 00000000000000..3419ee49abc0d1 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ReadyToRunCoreHeader.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +internal sealed class ReadyToRunCoreHeader : IData +{ + static ReadyToRunCoreHeader IData.Create(Target target, TargetPointer address) + => new ReadyToRunCoreHeader(target, address); + + public ReadyToRunCoreHeader(Target target, TargetPointer address) + { + Target.TypeInfo type = target.GetTypeInfo(DataType.ReadyToRunCoreHeader); + + NumberOfSections = target.Read(address + (ulong)type.Fields[nameof(NumberOfSections)].Offset); + Target.TypeInfo sectionType = target.GetTypeInfo(DataType.ReadyToRunSection); + for (int i = 0; i < NumberOfSections; i++) + { + TargetPointer sectionAddress = address + (ulong)(type.Size!.Value + i * sectionType.Size!.Value); + Sections.Add(target.ProcessedData.GetOrAdd(sectionAddress)); + } + } + + public uint NumberOfSections { get; init; } + public List Sections { get; } = []; +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ReadyToRunCoreInfo.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ReadyToRunCoreInfo.cs new file mode 100644 index 00000000000000..48b4b988463e08 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ReadyToRunCoreInfo.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +internal sealed class ReadyToRunCoreInfo : IData +{ + static ReadyToRunCoreInfo IData.Create(Target target, TargetPointer address) + => new ReadyToRunCoreInfo(target, address); + + public ReadyToRunCoreInfo(Target target, TargetPointer address) + { + Target.TypeInfo type = target.GetTypeInfo(DataType.ReadyToRunCoreInfo); + TargetPointer headerAddress = target.ReadPointer(address + (ulong)type.Fields[nameof(Header)].Offset); + Header = target.ProcessedData.GetOrAdd(headerAddress); + } + + public ReadyToRunCoreHeader Header { get; } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ReadyToRunInfo.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ReadyToRunInfo.cs index bce79429a10f19..ec460c242cb58e 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ReadyToRunInfo.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ReadyToRunInfo.cs @@ -34,6 +34,8 @@ public ReadyToRunInfo(Target target, TargetPointer address) // Map is from the composite info pointer (set to itself for non-multi-assembly composite images) EntryPointToMethodDescMap = CompositeInfo + (ulong)type.Fields[nameof(EntryPointToMethodDescMap)].Offset; + LoadedImageBase = target.ReadPointer(address + (ulong)type.Fields[nameof(LoadedImageBase)].Offset); + Composite = target.ReadPointer(address + (ulong)type.Fields[nameof(Composite)].Offset); } internal TargetPointer CompositeInfo { get; } @@ -49,4 +51,6 @@ public ReadyToRunInfo(Target target, TargetPointer address) public TargetPointer DelayLoadMethodCallThunks { get; } public TargetPointer DebugInfoSection { get; } public TargetPointer EntryPointToMethodDescMap { get; } + public TargetPointer LoadedImageBase { get; } + public TargetPointer Composite { get; } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ReadyToRunSection.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ReadyToRunSection.cs new file mode 100644 index 00000000000000..8fcc22401f9dca --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ReadyToRunSection.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +internal sealed class ReadyToRunSection : IData +{ + static ReadyToRunSection IData.Create(Target target, TargetPointer address) + => new ReadyToRunSection(target, address); + + public ReadyToRunSection(Target target, TargetPointer address) + { + Target.TypeInfo type = target.GetTypeInfo(DataType.ReadyToRunSection); + + Type = target.Read(address + (ulong)type.Fields[nameof(Type)].Offset); + Section = target.ProcessedData.GetOrAdd(address + (ulong)type.Fields[nameof(Section)].Offset); + } + + public uint Type { get; init; } + public ImageDataDirectory Section { get; init; } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/RealCodeHeader.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/RealCodeHeader.cs index 70fbccc0b52a7b..da639b342d4274 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/RealCodeHeader.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/RealCodeHeader.cs @@ -16,6 +16,8 @@ public RealCodeHeader(Target target, TargetPointer address) GCInfo = target.ReadPointer(address + (ulong)type.Fields[nameof(GCInfo)].Offset); NumUnwindInfos = target.Read(address + (ulong)type.Fields[nameof(NumUnwindInfos)].Offset); UnwindInfos = address + (ulong)type.Fields[nameof(UnwindInfos)].Offset; + TargetPointer jitEHInfoAddr = target.ReadPointer(address + (ulong)type.Fields[nameof(JitEHInfo)].Offset); + JitEHInfo = jitEHInfoAddr != TargetPointer.Null ? target.ProcessedData.GetOrAdd(jitEHInfoAddr) : null; } public TargetPointer MethodDesc { get; init; } @@ -23,4 +25,5 @@ public RealCodeHeader(Target target, TargetPointer address) public TargetPointer GCInfo { get; init; } public uint NumUnwindInfos { get; init; } public TargetPointer UnwindInfos { get; init; } + public EEILException? JitEHInfo { get; init; } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/EcmaMetadataUtils.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/EcmaMetadataUtils.cs index e49be30507f531..07a2f90fe0fa20 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/EcmaMetadataUtils.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/EcmaMetadataUtils.cs @@ -18,10 +18,14 @@ internal static class EcmaMetadataUtils // Metadata table index is the most significant byte of the 4-byte token public enum TokenType : uint { + mdtTypeRef = 0x01 << 24, + mdtTypeDef = 0x02 << 24, + mdtFieldDef = 0x04 << 24, mdtMethodDef = 0x06 << 24, - mdtFieldDef = 0x04 << 24 } + internal const uint TokenTypeMask = 0xff000000; + public static uint CreateMethodDef(uint tokenParts) { Debug.Assert((tokenParts & 0xff000000) == 0, $"Token type should not be set in {nameof(tokenParts)}"); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataAppDomain.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataAppDomain.cs index 770c86bdc0bb67..90d2b14c91a899 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataAppDomain.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataAppDomain.cs @@ -5,34 +5,152 @@ using System.Diagnostics; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; +using Microsoft.Diagnostics.DataContractReader.Contracts; namespace Microsoft.Diagnostics.DataContractReader.Legacy; [GeneratedComClass] public sealed unsafe partial class ClrDataAppDomain : IXCLRDataAppDomain { + private readonly Target _target; private readonly TargetPointer _appDomain; private readonly IXCLRDataAppDomain? _legacyImpl; public TargetPointer Address => _appDomain; - public ClrDataAppDomain(TargetPointer appDomain, IXCLRDataAppDomain? legacyImpl) + public ClrDataAppDomain(Target target, TargetPointer appDomain, IXCLRDataAppDomain? legacyImpl) { + _target = target; _appDomain = appDomain; _legacyImpl = legacyImpl; } - int IXCLRDataAppDomain.GetProcess(/*IXCLRDataProcess*/ void** process) + int IXCLRDataAppDomain.GetProcess(DacComNullableByRef process) => _legacyImpl is not null ? _legacyImpl.GetProcess(process) : HResults.E_NOTIMPL; int IXCLRDataAppDomain.GetName(uint bufLen, uint* nameLen, char* name) - => _legacyImpl is not null ? _legacyImpl.GetName(bufLen, nameLen, name) : HResults.E_NOTIMPL; + { + int hr = HResults.S_OK; + string friendlyName; + try + { + ILoader loader = _target.Contracts.Loader; + friendlyName = loader.GetAppDomainFriendlyName(); + } + catch (VirtualReadException) + { + // Match native DAC / SOSDacImpl behavior: fall back to empty string + // when the FriendlyName pointer targets unreadable memory. + friendlyName = string.Empty; + } + catch (System.Exception ex) + { + hr = ex.HResult; + friendlyName = string.Empty; + } + + if (hr >= 0) + { + OutputBufferHelpers.CopyStringToBuffer(name, bufLen, nameLen, friendlyName); + + // Match native DAC behavior: return S_FALSE when output is truncated. + uint requiredLen = (uint)friendlyName.Length + 1; + if (name is not null && bufLen > 0 && bufLen < requiredLen) + hr = HResults.S_FALSE; + } + +#if DEBUG + if (_legacyImpl is not null) + { + uint nameLenLocal; + char[] legacyNameBuf = new char[bufLen > 0 ? bufLen : 1]; + int hrLocal; + fixed (char* pLegacyName = legacyNameBuf) + { + hrLocal = _legacyImpl.GetName(bufLen, &nameLenLocal, name is not null ? pLegacyName : null); + } + + Debug.ValidateHResult(hr, hrLocal); + if (hr >= 0) + { + if (nameLen is not null) + Debug.Assert(*nameLen == nameLenLocal, $"cDAC: {*nameLen}, DAC: {nameLenLocal}"); + + if (name is not null && bufLen > 0) + { + // On truncation (S_FALSE), nameLenLocal is the full required length + // which may exceed bufLen. Cap to the actual buffer size. + int compareLen = (int)Math.Min(nameLenLocal, bufLen) - 1; + if (compareLen > 0) + { + string dacName = new string(legacyNameBuf, 0, compareLen); + string cdacName = new string(name, 0, compareLen); + Debug.Assert(dacName == cdacName, $"cDAC: {cdacName}, DAC: {dacName}"); + } + } + } + } +#endif + + return hr; + } int IXCLRDataAppDomain.GetUniqueID(ulong* id) - => _legacyImpl is not null ? _legacyImpl.GetUniqueID(id) : HResults.E_NOTIMPL; + { + int hr = HResults.S_OK; + try + { + if (id is null) + throw new ArgumentNullException(nameof(id)); + + *id = _target.ReadGlobal(Constants.Globals.DefaultADID); + } + catch (System.Exception ex) + { + hr = ex.HResult; + } + +#if DEBUG + if (_legacyImpl is not null && hr >= 0) + { + ulong idLocal; + int hrLocal = _legacyImpl.GetUniqueID(&idLocal); + Debug.ValidateHResult(hr, hrLocal); + Debug.Assert(*id == idLocal, $"cDAC: {*id}, DAC: {idLocal}"); + } +#endif + + return hr; + } int IXCLRDataAppDomain.GetFlags(uint* flags) - => _legacyImpl is not null ? _legacyImpl.GetFlags(flags) : HResults.E_NOTIMPL; + { + int hr = HResults.S_OK; + try + { + if (flags is null) + throw new ArgumentNullException(nameof(flags)); + + // CLRDATA_DOMAIN_DEFAULT = 0 + *flags = 0; + } + catch (System.Exception ex) + { + hr = ex.HResult; + } + +#if DEBUG + if (_legacyImpl is not null && hr >= 0) + { + uint flagsLocal; + int hrLocal = _legacyImpl.GetFlags(&flagsLocal); + Debug.ValidateHResult(hr, hrLocal); + Debug.Assert(*flags == flagsLocal, $"cDAC: {*flags}, DAC: {flagsLocal}"); + } +#endif + + return hr; + } int IXCLRDataAppDomain.IsSameObject(IXCLRDataAppDomain* appDomain) { @@ -46,7 +164,7 @@ int IXCLRDataAppDomain.IsSameObject(IXCLRDataAppDomain* appDomain) hr = _appDomain == other._appDomain ? HResults.S_OK : HResults.S_FALSE; } } - catch (Exception ex) + catch (System.Exception ex) { hr = ex.HResult; } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataFrame.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataFrame.cs index ced1465e9f1f26..5e325d3399ab01 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataFrame.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataFrame.cs @@ -8,6 +8,7 @@ using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; using Microsoft.Diagnostics.DataContractReader.Contracts; +using Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; namespace Microsoft.Diagnostics.DataContractReader.Legacy; @@ -38,10 +39,95 @@ int IXCLRDataFrame.GetContext( uint contextBufSize, uint* contextSize, [Out, MarshalUsing(CountElementName = nameof(contextBufSize))] byte[] contextBuf) - => _legacyImpl is not null ? _legacyImpl.GetContext(contextFlags, contextBufSize, contextSize, contextBuf) : HResults.E_NOTIMPL; + { + int hr = HResults.S_OK; + try + { + IStackWalk stackWalk = _target.Contracts.StackWalk; + byte[] context = stackWalk.GetRawContext(_dataFrame); + + if (contextSize is not null) + *contextSize = (uint)context.Length; + + // Match native DAC behavior: fail when the buffer is too small, + // and on success copy the full context. + if (contextBufSize < (uint)context.Length) + throw new ArgumentException(); + + if (contextBufSize > 0 && context.Length > 0) + Array.Copy(context, 0, contextBuf, 0, context.Length); + } + catch (System.Exception ex) + { + hr = ex.HResult; + } + +#if DEBUG + if (_legacyImpl is not null) + { + byte[] localContextBuf = new byte[contextBufSize]; + int hrLocal = _legacyImpl.GetContext(contextFlags, contextBufSize, null, localContextBuf); + Debug.ValidateHResult(hr, hrLocal); + + if (hr == HResults.S_OK) + { + IPlatformAgnosticContext contextStruct = IPlatformAgnosticContext.GetContextForPlatform(_target); + IPlatformAgnosticContext localContextStruct = IPlatformAgnosticContext.GetContextForPlatform(_target); + contextStruct.FillFromBuffer(contextBuf); + localContextStruct.FillFromBuffer(localContextBuf); + + Debug.Assert(contextStruct.Equals(localContextStruct)); + } + } +#endif + + return hr; + } - int IXCLRDataFrame.GetAppDomain(/*IXCLRDataAppDomain*/ void** appDomain) - => _legacyImpl is not null ? _legacyImpl.GetAppDomain(appDomain) : HResults.E_NOTIMPL; + int IXCLRDataFrame.GetAppDomain(DacComNullableByRef appDomain) + { + int hr = HResults.S_OK; + + int hrLegacy = HResults.S_OK; + IXCLRDataAppDomain? legacyAppDomain = null; + if (_legacyImpl is not null) + { + DacComNullableByRef legacyAppDomainOut = new(isNullRef: false); + hrLegacy = _legacyImpl.GetAppDomain(legacyAppDomainOut); + if (hrLegacy >= 0) + { + legacyAppDomain = legacyAppDomainOut.Interface; + } + } + + try + { + TargetPointer appDomainPointer = _target.ReadGlobalPointer(Constants.Globals.AppDomain); + TargetPointer appDomainAddr = _target.ReadPointer(appDomainPointer); + + if (appDomainAddr != TargetPointer.Null) + { + appDomain.Interface = new ClrDataAppDomain(_target, appDomainAddr, legacyAppDomain); + } + else + { + hr = HResults.S_FALSE; + } + } + catch (System.Exception ex) + { + hr = ex.HResult; + } + +#if DEBUG + if (_legacyImpl is not null) + { + Debug.ValidateHResult(hr, hrLegacy); + } +#endif + + return hr; + } int IXCLRDataFrame.GetNumArguments(uint* numArgs) { diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataTask.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataTask.cs index 0574546c87705d..99ac0153a4d0fa 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataTask.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataTask.cs @@ -37,7 +37,7 @@ int IXCLRDataTask.GetCurrentAppDomain(DacComNullableByRef ap try { TargetPointer currentAppDomain = _target.ReadPointer(_target.ReadGlobalPointer(Constants.Globals.AppDomain)); - appDomain.Interface = new ClrDataAppDomain(currentAppDomain, legacyAppDomain); + appDomain.Interface = new ClrDataAppDomain(_target, currentAppDomain, legacyAppDomain); } catch (System.Exception ex) { diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ISOSDacInterface.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ISOSDacInterface.cs index b645240cd13525..e88180432fe8d0 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ISOSDacInterface.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ISOSDacInterface.cs @@ -387,6 +387,13 @@ public struct DacpGcHeapAnalyzeData public int heap_analyze_success; // BOOL } +public struct DacpCOMInterfacePointerData +{ + public ClrDataAddress methodTable; + public ClrDataAddress interfacePtr; + public ClrDataAddress comContext; +} + [GeneratedComInterface] [Guid("286CA186-E763-4F61-9760-487D43AE4341")] public unsafe partial interface ISOSEnum @@ -493,6 +500,29 @@ public unsafe partial interface ISOSHandleEnum : ISOSEnum int Next(uint count, [In, Out, MarshalUsing(CountElementName = nameof(count))] SOSHandleData[] handles, uint* pNeeded); } +public struct DACEHInfo +{ + public enum EHClauseType : uint + { + EHFault, + EHFinally, + EHFilter, + EHTyped, + EHUnknown, + } + public EHClauseType clauseType; + public ClrDataAddress tryStartOffset; + public ClrDataAddress tryEndOffset; + public ClrDataAddress handlerStartOffset; + public ClrDataAddress handlerEndOffset; + public Interop.BOOL isDuplicateClause; + public ClrDataAddress filterOffset; + public Interop.BOOL isCatchAllHandler; + public ClrDataAddress moduleAddr; + public ClrDataAddress mtCatch; + public uint tokCatch; +} + public enum SOSStackSourceType : uint { SOS_StackSourceIP = 0, @@ -723,7 +753,7 @@ public unsafe partial interface ISOSDacInterface // EH [PreserveSig] - int TraverseEHInfo(ClrDataAddress ip, /*DUMPEHINFO*/ void* pCallback, void* token); + int TraverseEHInfo(ClrDataAddress ip, /*DUMPEHINFO*/ delegate* unmanaged pCallback, void* token); [PreserveSig] int GetNestedExceptionData(ClrDataAddress exception, ClrDataAddress* exceptionObject, ClrDataAddress* nextNestedException); @@ -753,7 +783,7 @@ public unsafe partial interface ISOSDacInterface [PreserveSig] int GetRCWData(ClrDataAddress addr, /*struct DacpRCWData */ void* data); [PreserveSig] - int GetRCWInterfaces(ClrDataAddress rcw, uint count, /*struct DacpCOMInterfacePointerData*/ void* interfaces, uint* pNeeded); + int GetRCWInterfaces(ClrDataAddress rcw, uint count, [In, Out, MarshalUsing(CountElementName = nameof(count))] DacpCOMInterfacePointerData[]? interfaces, uint* pNeeded); [PreserveSig] int GetCCWData(ClrDataAddress ccw, /*struct DacpCCWData */ void* data); [PreserveSig] @@ -795,13 +825,6 @@ public unsafe partial interface ISOSDacInterface int GetFailedAssemblyDisplayName(ClrDataAddress assembly, uint count, char* name, uint* pNeeded); }; -public struct DacpCOMInterfacePointerData -{ - public ClrDataAddress methodTable; - public ClrDataAddress interfacePtr; - public ClrDataAddress comContext; -} - #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value public struct DacpExceptionObjectData { diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/IXCLRData.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/IXCLRData.cs index ce62e874c6dc8c..862d9c6956b8c5 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/IXCLRData.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/IXCLRData.cs @@ -382,7 +382,7 @@ int GetContext( [Out, MarshalUsing(CountElementName = nameof(contextBufSize))] byte[] contextBuf); [PreserveSig] - int GetAppDomain(/*IXCLRDataAppDomain*/ void** appDomain); + int GetAppDomain(DacComNullableByRef appDomain); [PreserveSig] int GetNumArguments(uint* numArgs); @@ -587,7 +587,7 @@ int Request( public unsafe partial interface IXCLRDataAppDomain { [PreserveSig] - int GetProcess(/*IXCLRDataProcess*/ void** process); + int GetProcess(DacComNullableByRef process); [PreserveSig] int GetName(uint bufLen, uint* nameLen, char* name); [PreserveSig] diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs index 9753e6caae5a2a..38daa4317ddb15 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs @@ -3390,8 +3390,93 @@ int ISOSDacInterface.GetPrivateBinPaths(ClrDataAddress appDomain, int count, cha } int ISOSDacInterface.GetRCWData(ClrDataAddress addr, void* data) => _legacyImpl is not null ? _legacyImpl.GetRCWData(addr, data) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetRCWInterfaces(ClrDataAddress rcw, uint count, void* interfaces, uint* pNeeded) - => _legacyImpl is not null ? _legacyImpl.GetRCWInterfaces(rcw, count, interfaces, pNeeded) : HResults.E_NOTIMPL; + int ISOSDacInterface.GetRCWInterfaces(ClrDataAddress rcw, uint count, [In, MarshalUsing(CountElementName = nameof(count)), Out] DacpCOMInterfacePointerData[]? interfaces, uint* pNeeded) + { + int hr = HResults.S_OK; +#if DEBUG + int numWritten = 0; +#endif + try + { + if (rcw == 0) + throw new ArgumentException(); + + TargetPointer rcwPtr = rcw.ToTargetPointer(_target); + IBuiltInCOM builtInCom = _target.Contracts.BuiltInCOM; // E_NOTIMPL if not defined (non-Windows) + IEnumerable<(TargetPointer MethodTable, TargetPointer Unknown)> entries = builtInCom.GetRCWInterfaces(rcwPtr); + + if (interfaces == null) + { + if (pNeeded == null) + { + throw new ArgumentException(); + } + else + { + *pNeeded = (uint)entries.Count(); +#if DEBUG + numWritten = (int)*pNeeded; +#endif + } + } + else + { + TargetPointer ctxCookie = builtInCom.GetRCWContext(rcwPtr); + uint itemIndex = 0; + foreach (var (methodTable, unknown) in entries) + { + if (itemIndex >= count) + { +#if DEBUG + numWritten = (int)itemIndex; +#endif + throw new ArgumentException(); + } + + interfaces[itemIndex].methodTable = methodTable.ToClrDataAddress(_target); + interfaces[itemIndex].interfacePtr = unknown.ToClrDataAddress(_target); + interfaces[itemIndex].comContext = ctxCookie.ToClrDataAddress(_target); + itemIndex++; + } + + if (pNeeded != null) + *pNeeded = itemIndex; +#if DEBUG + numWritten = (int)itemIndex; +#endif + } + } + catch (System.Exception ex) + { + hr = ex.HResult; + } + +#if DEBUG + if (_legacyImpl is not null) + { + uint pNeededLocal = 0; + int hrLocal; + DacpCOMInterfacePointerData[]? interfacesLocal = interfaces != null ? new DacpCOMInterfacePointerData[count] : null; + hrLocal = _legacyImpl.GetRCWInterfaces(rcw, count, interfacesLocal, pNeeded == null && interfacesLocal == null ? null : &pNeededLocal); + Debug.ValidateHResult(hr, hrLocal); + if (hr == HResults.S_OK) + { + Debug.Assert(numWritten == pNeededLocal, $"cDAC: {numWritten}, DAC: {pNeededLocal}"); + if (interfacesLocal is not null && interfaces is not null) + { + for (int i = 0; i < (int)pNeededLocal; i++) + { + Debug.Assert(interfaces[i].methodTable == interfacesLocal![i].methodTable, $"cDAC: {interfaces[i].methodTable:x}, DAC: {interfacesLocal[i].methodTable:x}"); + Debug.Assert(interfaces[i].interfacePtr == interfacesLocal![i].interfacePtr, $"cDAC: {interfaces[i].interfacePtr:x}, DAC: {interfacesLocal[i].interfacePtr:x}"); + Debug.Assert(interfaces[i].comContext == interfacesLocal![i].comContext, $"cDAC: {interfaces[i].comContext:x}, DAC: {interfacesLocal[i].comContext:x}"); + } + } + } + } +#endif + + return hr; + } int ISOSDacInterface.GetRegisterName(int regName, uint count, char* buffer, uint* pNeeded) { int hr = HResults.S_OK; @@ -3498,7 +3583,7 @@ int ISOSDacInterface.GetRegisterName(int regName, uint count, char* buffer, uint private static readonly string[] s_riscV64Registers = [ - "R0", "RA", "SP", "GP", + "zero", "RA", "SP", "GP", "TP", "T0", "T1", "T2", "FP", "S1", "A0", "A1", "A2", "A3", "A4", "A5", @@ -4024,8 +4109,142 @@ int ISOSDacInterface.GetWorkRequestData(ClrDataAddress addrWorkRequest, void* da return hr; } - int ISOSDacInterface.TraverseEHInfo(ClrDataAddress ip, void* pCallback, void* token) - => _legacyImpl is not null ? _legacyImpl.TraverseEHInfo(ip, pCallback, token) : HResults.E_NOTIMPL; + +#if DEBUG + internal sealed class TraverseEhInfoExpected + { + public TraverseEhInfoExpected(List elements, bool expectAbort, uint? abortIndex = null) + { + Elements = elements; + ExpectAbort = expectAbort; + AbortIndex = abortIndex; + } + + public List Elements { get; } + public bool ExpectAbort { get; } + public uint? AbortIndex { get; } + public int CallbackCount { get; set; } + } + + [UnmanagedCallersOnly] + private static int TraverseEHInfoCallback(uint clauseIndex, uint totalClauses, DACEHInfo* pEHInfo, void* expectedEhInfo) + { + var expected = (TraverseEhInfoExpected)GCHandle.FromIntPtr((nint)expectedEhInfo).Target!; + Debug.Assert(clauseIndex < totalClauses, $"Invalid clause index {clauseIndex} of {totalClauses}"); + if (clauseIndex < expected.Elements.Count) + { + DACEHInfo expectedEhClause = expected.Elements[(int)clauseIndex]; + Debug.Assert(pEHInfo->clauseType == expectedEhClause.clauseType, $"cDAC: {expectedEhClause.clauseType}, DAC: {pEHInfo->clauseType}"); + Debug.Assert(pEHInfo->tryStartOffset == expectedEhClause.tryStartOffset, $"cDAC: {expectedEhClause.tryStartOffset:x}, DAC: {pEHInfo->tryStartOffset:x}"); + Debug.Assert(pEHInfo->tryEndOffset == expectedEhClause.tryEndOffset, $"cDAC: {expectedEhClause.tryEndOffset:x}, DAC: {pEHInfo->tryEndOffset:x}"); + Debug.Assert(pEHInfo->handlerStartOffset == expectedEhClause.handlerStartOffset, $"cDAC: {expectedEhClause.handlerStartOffset:x}, DAC: {pEHInfo->handlerStartOffset:x}"); + Debug.Assert(pEHInfo->handlerEndOffset == expectedEhClause.handlerEndOffset, $"cDAC: {expectedEhClause.handlerEndOffset:x}, DAC: {pEHInfo->handlerEndOffset:x}"); + Debug.Assert(pEHInfo->isDuplicateClause == expectedEhClause.isDuplicateClause, $"cDAC: {expectedEhClause.isDuplicateClause}, DAC: {pEHInfo->isDuplicateClause}"); + Debug.Assert(pEHInfo->filterOffset == expectedEhClause.filterOffset, $"cDAC: {expectedEhClause.filterOffset:x}, DAC: {pEHInfo->filterOffset:x}"); + Debug.Assert(pEHInfo->isCatchAllHandler == expectedEhClause.isCatchAllHandler, $"cDAC: {expectedEhClause.isCatchAllHandler}, DAC: {pEHInfo->isCatchAllHandler}"); + Debug.Assert(pEHInfo->moduleAddr == expectedEhClause.moduleAddr, $"cDAC: {expectedEhClause.moduleAddr:x}, DAC: {pEHInfo->moduleAddr:x}"); + Debug.Assert(pEHInfo->mtCatch == expectedEhClause.mtCatch, $"cDAC: {expectedEhClause.mtCatch:x}, DAC: {pEHInfo->mtCatch:x}"); + Debug.Assert(pEHInfo->tokCatch == expectedEhClause.tokCatch, $"cDAC: {expectedEhClause.tokCatch:x}, DAC: {pEHInfo->tokCatch:x}"); + } + else + { + Debug.Fail($"Received unexpected clause index {clauseIndex} of {totalClauses}"); + } + + expected.CallbackCount++; + + if (expected.ExpectAbort && expected.AbortIndex == clauseIndex) + { + return 0; // Return 0 to trigger E_ABORT and stop enumeration + } + return 1; // Return non-zero to continue enumeration + } +#endif + int ISOSDacInterface.TraverseEHInfo(ClrDataAddress ip, delegate* unmanaged pCallback, void* token) + { + int hr = HResults.S_OK; +#if DEBUG + List clausesLocal = new(); +#endif + int E_ABORT = unchecked((int)0x80004004); + uint lastIndex = 0; + + try + { + if (ip == 0 || pCallback == null) + { + throw new ArgumentException(); + } + + IExecutionManager executionManager = _target.Contracts.ExecutionManager; + + CodeBlockHandle? handle = executionManager.GetCodeBlockHandle(ip.ToTargetCodePointer(_target)); + if (handle is not CodeBlockHandle codeBlockHandle) + { + throw new ArgumentException(); + } + + List exceptionClauses = executionManager.GetExceptionClauses(codeBlockHandle); + uint numClauses = (uint)exceptionClauses.Count; + for (uint i = 0; i < numClauses; i++) + { + ExceptionClauseInfo clause = exceptionClauses[(int)i]; + DACEHInfo ehInfo = default; + ehInfo.clauseType = clause.ClauseType switch + { + ExceptionClauseInfo.ExceptionClauseFlags.Fault => DACEHInfo.EHClauseType.EHFault, + ExceptionClauseInfo.ExceptionClauseFlags.Finally => DACEHInfo.EHClauseType.EHFinally, + ExceptionClauseInfo.ExceptionClauseFlags.Filter => DACEHInfo.EHClauseType.EHFilter, + ExceptionClauseInfo.ExceptionClauseFlags.Typed => DACEHInfo.EHClauseType.EHTyped, + _ => DACEHInfo.EHClauseType.EHUnknown, + }; + + ehInfo.filterOffset = clause.FilterOffset is uint filterOffset ? (ulong)filterOffset : 0; + ehInfo.isCatchAllHandler = clause.IsCatchAllHandler is true ? Interop.BOOL.TRUE : Interop.BOOL.FALSE; + ehInfo.tokCatch = clause.ClassToken is uint classToken ? classToken : 0; + ehInfo.moduleAddr = clause.ModuleAddr is TargetPointer moduleAddr ? moduleAddr.ToClrDataAddress(_target) : 0; + ehInfo.mtCatch = clause.TypeHandle is TargetNUInt th ? new TargetPointer(th.Value).ToClrDataAddress(_target) : 0; + + ehInfo.tryStartOffset = (ulong)clause.TryStartPC; + ehInfo.tryEndOffset = (ulong)clause.TryEndPC; + ehInfo.handlerStartOffset = (ulong)clause.HandlerStartPC; + ehInfo.handlerEndOffset = (ulong)clause.HandlerEndPC; +#if DEBUG + clausesLocal.Add(ehInfo); +#endif + if (pCallback(i, numClauses, &ehInfo, token) == 0) + { + lastIndex = i; + throw Marshal.GetExceptionForHR(E_ABORT)!; + } + } + } + catch (System.Exception ex) + { + hr = ex.HResult; + } + +#if DEBUG + if (_legacyImpl is not null) + { + TraverseEhInfoExpected expected = new(clausesLocal, hr == E_ABORT, hr == E_ABORT ? lastIndex : null); + GCHandle expectedHandle = GCHandle.Alloc(expected); + try + { + void* tokenDebug = GCHandle.ToIntPtr(expectedHandle).ToPointer(); + delegate* unmanaged callbackDebugPtr = &TraverseEHInfoCallback; + + int hrLocal = _legacyImpl.TraverseEHInfo(ip, callbackDebugPtr, tokenDebug); + Debug.ValidateHResult(hr, hrLocal, HResultValidationMode.Exact); + } + finally + { + expectedHandle.Free(); + } + } +#endif + return hr; + } int ISOSDacInterface.TraverseLoaderHeap(ClrDataAddress loaderHeapAddr, void* pCallback) => _legacyImpl is not null ? _legacyImpl.TraverseLoaderHeap(loaderHeapAddr, pCallback) : HResults.E_NOTIMPL; @@ -4119,7 +4338,7 @@ int ISOSDacInterface.TraverseRCWCleanupList(ClrDataAddress cleanupListPtr, deleg if (pCallback is null) throw new ArgumentException(); - Contracts.IBuiltInCOM contract = _target.Contracts.BuiltInCOM; + Contracts.IBuiltInCOM contract = _target.Contracts.BuiltInCOM; // E_NOTIMPL if not defined (non-Windows) TargetPointer listPtr = cleanupListPtr.ToTargetPointer(_target); cleanupInfos = contract.GetRCWCleanupList(listPtr); diff --git a/src/native/managed/cdac/tests/BuiltInCOMTests.cs b/src/native/managed/cdac/tests/BuiltInCOMTests.cs index 3da3ec04d531de..f2b9e86c81e5ef 100644 --- a/src/native/managed/cdac/tests/BuiltInCOMTests.cs +++ b/src/native/managed/cdac/tests/BuiltInCOMTests.cs @@ -12,6 +12,105 @@ namespace Microsoft.Diagnostics.DataContractReader.Tests; public class BuiltInCOMTests { + private const ulong AllocationRangeStart = 0x00000000_20000000; + private const ulong AllocationRangeEnd = 0x00000000_30000000; + + private const uint TestRCWInterfaceCacheSize = 8; + + private static readonly MockDescriptors.TypeFields RCWFields = new MockDescriptors.TypeFields() + { + DataType = DataType.RCW, + Fields = + [ + new(nameof(Data.RCW.NextCleanupBucket), DataType.pointer), + new(nameof(Data.RCW.NextRCW), DataType.pointer), + new(nameof(Data.RCW.Flags), DataType.uint32), + new(nameof(Data.RCW.CtxCookie), DataType.pointer), + new(nameof(Data.RCW.CtxEntry), DataType.pointer), + new(nameof(Data.RCW.InterfaceEntries), DataType.pointer), + ] + }; + + private static readonly MockDescriptors.TypeFields InterfaceEntryFields = new MockDescriptors.TypeFields() + { + DataType = DataType.InterfaceEntry, + Fields = + [ + new(nameof(Data.InterfaceEntry.MethodTable), DataType.pointer), + new(nameof(Data.InterfaceEntry.Unknown), DataType.pointer), + ] + }; + + private static void BuiltInCOMContractHelper( + MockTarget.Architecture arch, + Action> configure, + Action testCase) + { + TargetTestHelpers targetTestHelpers = new(arch); + MockMemorySpace.Builder builder = new(targetTestHelpers); + + Dictionary types = MockDescriptors.GetTypesForTypeFields( + targetTestHelpers, + [RCWFields, InterfaceEntryFields]); + + configure(builder, targetTestHelpers, types); + + (string Name, ulong Value)[] globals = + [ + (nameof(Constants.Globals.RCWInterfaceCacheSize), TestRCWInterfaceCacheSize), + ]; + + var target = new TestPlaceholderTarget(arch, builder.GetMemoryContext().ReadFromTarget, types, globals); + target.SetContracts(Mock.Of( + c => c.BuiltInCOM == ((IContractFactory)new BuiltInCOMFactory()).CreateContract(target, 1))); + + testCase(target); + } + + /// + /// Allocates an RCW mock with the interface entries embedded inline (matching the real C++ layout + /// where m_aInterfaceEntries is an inline array within the RCW struct). + /// Returns the address of the RCW. + /// + private static TargetPointer AddRCWWithInlineEntries( + MockMemorySpace.Builder builder, + TargetTestHelpers targetTestHelpers, + Dictionary types, + MockMemorySpace.BumpAllocator allocator, + (TargetPointer MethodTable, TargetPointer Unknown)[] entries, + TargetPointer ctxCookie = default) + { + Target.TypeInfo rcwTypeInfo = types[DataType.RCW]; + Target.TypeInfo entryTypeInfo = types[DataType.InterfaceEntry]; + uint entrySize = entryTypeInfo.Size!.Value; + uint entriesOffset = (uint)rcwTypeInfo.Fields[nameof(Data.RCW.InterfaceEntries)].Offset; + + // The RCW block must be large enough to hold the RCW header plus all inline entries + uint totalSize = entriesOffset + entrySize * TestRCWInterfaceCacheSize; + MockMemorySpace.HeapFragment fragment = allocator.Allocate(totalSize, "RCW with inline entries"); + Span data = fragment.Data; + + // Write RCW header fields + targetTestHelpers.WritePointer( + data.Slice(rcwTypeInfo.Fields[nameof(Data.RCW.CtxCookie)].Offset), + ctxCookie); + + // Write the inline interface entries starting at entriesOffset + for (int i = 0; i < entries.Length && i < TestRCWInterfaceCacheSize; i++) + { + Span entryData = data.Slice((int)(entriesOffset + i * entrySize)); + targetTestHelpers.WritePointer( + entryData.Slice(entryTypeInfo.Fields[nameof(Data.InterfaceEntry.MethodTable)].Offset), + entries[i].MethodTable); + targetTestHelpers.WritePointer( + entryData.Slice(entryTypeInfo.Fields[nameof(Data.InterfaceEntry.Unknown)].Offset), + entries[i].Unknown); + } + + builder.AddHeapFragment(fragment); + return fragment.Address; + } + // Flag values matching the C++ runtime private const ulong IsLayoutCompleteFlag = 0x10; @@ -473,6 +572,40 @@ public void GetCCWInterfaces_LinkedWrapper_WalksFullChainFromAnyWrapper(MockTarg } } + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void GetRCWInterfaces_ReturnsFilledEntries(MockTarget.Architecture arch) + { + TargetPointer rcwAddress = default; + (TargetPointer MethodTable, TargetPointer Unknown)[] expectedEntries = + [ + (new TargetPointer(0x1000), new TargetPointer(0x2000)), + (new TargetPointer(0x3000), new TargetPointer(0x4000)), + ]; + + BuiltInCOMContractHelper(arch, + (builder, targetTestHelpers, types) => + { + MockMemorySpace.BumpAllocator allocator = builder.CreateAllocator(AllocationRangeStart, AllocationRangeEnd); + rcwAddress = AddRCWWithInlineEntries(builder, targetTestHelpers, types, allocator, expectedEntries); + }, + (target) => + { + IBuiltInCOM contract = target.Contracts.BuiltInCOM; + Assert.NotNull(contract); + + List<(TargetPointer MethodTable, TargetPointer Unknown)> results = + contract.GetRCWInterfaces(rcwAddress).ToList(); + + Assert.Equal(expectedEntries.Length, results.Count); + for (int i = 0; i < expectedEntries.Length; i++) + { + Assert.Equal(expectedEntries[i].MethodTable, results[i].MethodTable); + Assert.Equal(expectedEntries[i].Unknown, results[i].Unknown); + } + }); + } + [Theory] [ClassData(typeof(MockTarget.StdArch))] public void GetCCWInterfaces_ComIpAddress_ResolvesToCCW(MockTarget.Architecture arch) @@ -566,6 +699,84 @@ public void GetCCWInterfaces_ComIpAddress_ResolvesToCCW(MockTarget.Architecture } } + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void GetRCWInterfaces_SkipsEntriesWithNullUnknown(MockTarget.Architecture arch) + { + TargetPointer rcwAddress = default; + // The IsFree() check uses only Unknown == null; entries with Unknown == null are skipped. + (TargetPointer MethodTable, TargetPointer Unknown)[] entries = + [ + (new TargetPointer(0x1000), new TargetPointer(0x2000)), + (TargetPointer.Null, TargetPointer.Null), // free entry (Unknown == null) + (new TargetPointer(0x5000), new TargetPointer(0x6000)), + ]; + + BuiltInCOMContractHelper(arch, + (builder, targetTestHelpers, types) => + { + MockMemorySpace.BumpAllocator allocator = builder.CreateAllocator(AllocationRangeStart, AllocationRangeEnd); + rcwAddress = AddRCWWithInlineEntries(builder, targetTestHelpers, types, allocator, entries); + }, + (target) => + { + IBuiltInCOM contract = target.Contracts.BuiltInCOM; + List<(TargetPointer MethodTable, TargetPointer Unknown)> results = + contract.GetRCWInterfaces(rcwAddress).ToList(); + + // Only the 2 entries with non-null Unknown are returned + Assert.Equal(2, results.Count); + Assert.Equal(new TargetPointer(0x1000), results[0].MethodTable); + Assert.Equal(new TargetPointer(0x2000), results[0].Unknown); + Assert.Equal(new TargetPointer(0x5000), results[1].MethodTable); + Assert.Equal(new TargetPointer(0x6000), results[1].Unknown); + }); + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void GetRCWInterfaces_EmptyCache_ReturnsEmpty(MockTarget.Architecture arch) + { + TargetPointer rcwAddress = default; + + BuiltInCOMContractHelper(arch, + (builder, targetTestHelpers, types) => + { + MockMemorySpace.BumpAllocator allocator = builder.CreateAllocator(AllocationRangeStart, AllocationRangeEnd); + rcwAddress = AddRCWWithInlineEntries(builder, targetTestHelpers, types, allocator, []); + }, + (target) => + { + IBuiltInCOM contract = target.Contracts.BuiltInCOM; + List<(TargetPointer MethodTable, TargetPointer Unknown)> results = + contract.GetRCWInterfaces(rcwAddress).ToList(); + + Assert.Empty(results); + }); + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void GetRCWContext_ReturnsCtxCookie(MockTarget.Architecture arch) + { + TargetPointer rcwAddress = default; + TargetPointer expectedCookie = new TargetPointer(0xC00C_1E00); + + BuiltInCOMContractHelper(arch, + (builder, targetTestHelpers, types) => + { + MockMemorySpace.BumpAllocator allocator = builder.CreateAllocator(AllocationRangeStart, AllocationRangeEnd); + rcwAddress = AddRCWWithInlineEntries(builder, targetTestHelpers, types, allocator, [], expectedCookie); + }, + (target) => + { + IBuiltInCOM contract = target.Contracts.BuiltInCOM; + TargetPointer result = contract.GetRCWContext(rcwAddress); + + Assert.Equal(expectedCookie, result); + }); + } + [Theory] [ClassData(typeof(MockTarget.StdArch))] public void GetCCWFromInterfacePointer_SCCWIp_ResolvesToStartCCW(MockTarget.Architecture arch) diff --git a/src/native/managed/cdac/tests/DumpTests/Debuggees/ExceptionHandlingInfo/ExceptionHandlingInfo.csproj b/src/native/managed/cdac/tests/DumpTests/Debuggees/ExceptionHandlingInfo/ExceptionHandlingInfo.csproj new file mode 100644 index 00000000000000..bb776824769fe6 --- /dev/null +++ b/src/native/managed/cdac/tests/DumpTests/Debuggees/ExceptionHandlingInfo/ExceptionHandlingInfo.csproj @@ -0,0 +1,5 @@ + + + Full + + diff --git a/src/native/managed/cdac/tests/DumpTests/Debuggees/ExceptionHandlingInfo/Program.cs b/src/native/managed/cdac/tests/DumpTests/Debuggees/ExceptionHandlingInfo/Program.cs new file mode 100644 index 00000000000000..a8a030ef90f993 --- /dev/null +++ b/src/native/managed/cdac/tests/DumpTests/Debuggees/ExceptionHandlingInfo/Program.cs @@ -0,0 +1,50 @@ +// 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.Runtime.CompilerServices; + +/// +/// Debuggee for cDAC dump tests — exercises the ExecutionManager EH clause enumeration. +/// The CrashInExceptionHandler method has a try/catch with filter, a typed catch, +/// a catch-all handler (catch without a type), and a finally block, +/// then calls FailFast from the finally to produce the dump. +/// +internal static class Program +{ + private static void Main() + { + CrashInExceptionHandler(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void CrashInExceptionHandler() + { + try + { + try + { + try + { + throw new NotImplementedException("bad input"); + } + catch (NotImplementedException ex) when (ex.Message.Contains("good")) + { + Console.WriteLine($"caught: {ex.Message}"); + } + } + catch (NotImplementedException ex) + { + Console.WriteLine($"outer caught: {ex.Message}"); + } + } + catch + { + Console.WriteLine("catch-all handler"); + } + finally + { + Environment.FailFast("cDAC dump test: ExceptionHandlingInfo debuggee intentional crash"); + } + } +} diff --git a/src/native/managed/cdac/tests/DumpTests/Debuggees/RCWInterfaces/Program.cs b/src/native/managed/cdac/tests/DumpTests/Debuggees/RCWInterfaces/Program.cs new file mode 100644 index 00000000000000..de77918bc6e304 --- /dev/null +++ b/src/native/managed/cdac/tests/DumpTests/Debuggees/RCWInterfaces/Program.cs @@ -0,0 +1,119 @@ +// 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.Runtime.InteropServices; +using System.Runtime.Versioning; + +/// +/// Debuggee for cDAC dump tests — exercises the BuiltInCOM contract's GetRCWInterfaces API. +/// Creates a real RCW for an unmanaged COM object with populated interface cache, +/// then crashes to produce a dump for analysis. +/// This debuggee is Windows-only, as RCW support requires FEATURE_COMINTEROP. +/// +internal static partial class Program +{ + private const int S_OK = 0; + private const int S_FALSE = 1; + private const int RpcEChangedMode = unchecked((int)0x80010106); + private const uint CoInitMultithreaded = 0; + + private static readonly Guid CLSID_StdGlobalInterfaceTable = new("00000323-0000-0000-C000-000000000046"); + private static readonly Guid IID_IUnknown = new("00000000-0000-0000-C000-000000000046"); + + [ComImport] + [Guid("00000146-0000-0000-C000-000000000046")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + private interface IGlobalInterfaceTable + { + [PreserveSig] + int RegisterInterfaceInGlobal(IntPtr pUnk, ref Guid riid, out int pdwCookie); + + [PreserveSig] + int RevokeInterfaceFromGlobal(int dwCookie); + + [PreserveSig] + int GetInterfaceFromGlobal(int dwCookie, ref Guid riid, out IntPtr ppv); + } + + [LibraryImport("ole32.dll")] + private static partial int CoInitializeEx(IntPtr pvReserved, uint dwCoInit); + + [LibraryImport("ole32.dll")] + private static partial void CoUninitialize(); + + private static void Main() + { + if (OperatingSystem.IsWindows()) + { + CreateRcwOnWindows(); + } + + Environment.FailFast("cDAC dump test: RCWInterfaces debuggee intentional crash"); + } + + [SupportedOSPlatform("windows")] + private static void CreateRcwOnWindows() + { + bool callCoUninitialize = false; + int coInitializeResult = CoInitializeEx(IntPtr.Zero, CoInitMultithreaded); + if (coInitializeResult == S_OK || coInitializeResult == S_FALSE) + { + callCoUninitialize = true; + } + else if (coInitializeResult != RpcEChangedMode) + { + Marshal.ThrowExceptionForHR(coInitializeResult); + } + + IntPtr rcwIUnknown = IntPtr.Zero; + IntPtr fetchedIUnknown = IntPtr.Zero; + GCHandle rcwHandle = default; + + try + { + Type comType = Type.GetTypeFromCLSID(CLSID_StdGlobalInterfaceTable, throwOnError: true)!; + object rcwObject = Activator.CreateInstance(comType)!; + + IGlobalInterfaceTable globalInterfaceTable = (IGlobalInterfaceTable)rcwObject; + + rcwIUnknown = Marshal.GetIUnknownForObject(rcwObject); + + Guid iidIUnknown = IID_IUnknown; + int registerResult = globalInterfaceTable.RegisterInterfaceInGlobal(rcwIUnknown, ref iidIUnknown, out int cookie); + Marshal.ThrowExceptionForHR(registerResult); + + int getResult = globalInterfaceTable.GetInterfaceFromGlobal(cookie, ref iidIUnknown, out fetchedIUnknown); + Marshal.ThrowExceptionForHR(getResult); + + int revokeResult = globalInterfaceTable.RevokeInterfaceFromGlobal(cookie); + Marshal.ThrowExceptionForHR(revokeResult); + + // Pin the RCW object in a strong GC handle so the dump test can find it + // by walking the strong handle table (matching how GCRoots debuggee works). + rcwHandle = GCHandle.Alloc(rcwObject, GCHandleType.Normal); + GC.KeepAlive(globalInterfaceTable); + GC.KeepAlive(rcwHandle); + GC.KeepAlive(rcwObject); + } + finally + { + if (fetchedIUnknown != IntPtr.Zero) + { + Marshal.Release(fetchedIUnknown); + } + + if (rcwIUnknown != IntPtr.Zero) + { + Marshal.Release(rcwIUnknown); + } + + GC.KeepAlive(rcwHandle); + + if (callCoUninitialize) + { + CoUninitialize(); + } + } + } +} diff --git a/src/native/managed/cdac/tests/DumpTests/Debuggees/RCWInterfaces/RCWInterfaces.csproj b/src/native/managed/cdac/tests/DumpTests/Debuggees/RCWInterfaces/RCWInterfaces.csproj new file mode 100644 index 00000000000000..69a9ea88447b47 --- /dev/null +++ b/src/native/managed/cdac/tests/DumpTests/Debuggees/RCWInterfaces/RCWInterfaces.csproj @@ -0,0 +1,7 @@ + + + + $(NoWarn);CA1416 + Full + + diff --git a/src/native/managed/cdac/tests/DumpTests/Debuggees/StackWalk/Program.cs b/src/native/managed/cdac/tests/DumpTests/Debuggees/StackWalk/Program.cs index 7fed5894a501b7..d7d671375aacf5 100644 --- a/src/native/managed/cdac/tests/DumpTests/Debuggees/StackWalk/Program.cs +++ b/src/native/managed/cdac/tests/DumpTests/Debuggees/StackWalk/Program.cs @@ -15,19 +15,31 @@ internal static class Program private static void Main() { - MethodA(); + MethodA(0); } [MethodImpl(MethodImplOptions.NoInlining)] - private static void MethodA() + private static void MethodA(int depth) { + if (depth < 0) + return; MethodB(); } [MethodImpl(MethodImplOptions.NoInlining)] private static void MethodB() { - MethodC(); + // The try/finally ensures localObj is stored as a real IL local variable, + // because the evaluation stack is empty at finally-block entry per ECMA-335. + object localObj = new object(); + try + { + MethodC(); + } + finally + { + GC.KeepAlive(localObj); + } } [MethodImpl(MethodImplOptions.NoInlining)] diff --git a/src/native/managed/cdac/tests/DumpTests/DumpInfo.cs b/src/native/managed/cdac/tests/DumpTests/DumpInfo.cs index 8ca72d6760c760..8296593db723be 100644 --- a/src/native/managed/cdac/tests/DumpTests/DumpInfo.cs +++ b/src/native/managed/cdac/tests/DumpTests/DumpInfo.cs @@ -64,6 +64,8 @@ public static DumpInfo ForCurrentMachine() System.Runtime.InteropServices.Architecture.X86 => "x86", System.Runtime.InteropServices.Architecture.Arm64 => "arm64", System.Runtime.InteropServices.Architecture.Arm => "arm", + System.Runtime.InteropServices.Architecture.RiscV64 => "riscv64", + System.Runtime.InteropServices.Architecture.LoongArch64 => "loongarch64", _ => "unknown", }; diff --git a/src/native/managed/cdac/tests/DumpTests/DumpTestBase.cs b/src/native/managed/cdac/tests/DumpTests/DumpTestBase.cs index 6c4b4db9cc6c6d..1b02d1debfc59a 100644 --- a/src/native/managed/cdac/tests/DumpTests/DumpTestBase.cs +++ b/src/native/managed/cdac/tests/DumpTests/DumpTestBase.cs @@ -68,14 +68,22 @@ public static IEnumerable TestConfigurations /// attributes on the calling test method. Call this as the first line of every test. /// protected void InitializeDumpTest(TestConfiguration config, [CallerMemberName] string callerName = "") + => InitializeDumpTest(config, DebuggeeName, DumpType, callerName); + + /// + /// Loads the dump for the given using an explicit + /// and . + /// Use this overload when individual test methods need different debuggees. + /// + protected void InitializeDumpTest(TestConfiguration config, string debuggeeName, string dumpType, [CallerMemberName] string callerName = "") { string dumpRoot = GetDumpRoot(); string versionDir = Path.Combine(dumpRoot, config.RuntimeVersion); _dumpInfo = DumpInfo.TryLoad(versionDir); - EvaluateSkipAttributes(config, callerName); + EvaluateSkipAttributes(config, callerName, dumpType); - string dumpPath = Path.Combine(versionDir, DumpType, DebuggeeName, $"{DebuggeeName}.dmp"); + string dumpPath = Path.Combine(versionDir, dumpType, debuggeeName, $"{debuggeeName}.dmp"); Assert.True(File.Exists(dumpPath), $"Dump file not found: {dumpPath}"); @@ -103,9 +111,9 @@ public void Dispose() /// Checks the calling test method for skip attributes and throws /// if the current configuration matches. /// - private void EvaluateSkipAttributes(TestConfiguration config, string callerName) + private void EvaluateSkipAttributes(TestConfiguration config, string callerName, string? dumpType = null) { - if (config.RuntimeVersion is "net10.0" && DumpType == "heap") + if (config.RuntimeVersion is "net10.0" && (dumpType ?? DumpType) == "heap") { throw new SkipTestException($"[net10.0] Skipping heap dump tests due to outdated dump generation."); } diff --git a/src/native/managed/cdac/tests/DumpTests/ExceptionHandlingInfoDumpTests.cs b/src/native/managed/cdac/tests/DumpTests/ExceptionHandlingInfoDumpTests.cs new file mode 100644 index 00000000000000..53a84eb3c3f1b4 --- /dev/null +++ b/src/native/managed/cdac/tests/DumpTests/ExceptionHandlingInfoDumpTests.cs @@ -0,0 +1,172 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Linq; +using Microsoft.Diagnostics.DataContractReader.Contracts; +using Xunit; + +namespace Microsoft.Diagnostics.DataContractReader.DumpTests; + +/// +/// Dump-based integration tests for the ExecutionManager EH clause enumeration. +/// Uses the ExceptionHandlingInfo debuggee, which has a method with: +/// - A filter clause (catch-when) +/// - A typed catch clause +/// - A catch-all handler (catch without a type) +/// - A finally clause +/// The debuggee crashes via FailFast inside the finally block. +/// +public class ExceptionHandlingInfoDumpTests : DumpTestBase +{ + protected override string DebuggeeName => "ExceptionHandlingInfo"; + protected override string DumpType => "full"; + + /// + /// Finds the CodeBlockHandle for the CrashInExceptionHandler method by walking + /// the crashing thread's stack, resolving method names, then using the method's + /// native code entry point to obtain a CodeBlockHandle. + /// + private CodeBlockHandle FindCrashMethodCodeBlock() + { + IExecutionManager executionManager = Target.Contracts.ExecutionManager; + IRuntimeTypeSystem rts = Target.Contracts.RuntimeTypeSystem; + IStackWalk stackWalk = Target.Contracts.StackWalk; + + ThreadData crashingThread = DumpTestHelpers.FindFailFastThread(Target); + + foreach (IStackDataFrameHandle frame in stackWalk.CreateStackWalk(crashingThread)) + { + TargetPointer methodDescPtr = stackWalk.GetMethodDescPtr(frame); + string? name = DumpTestHelpers.GetMethodName(Target, methodDescPtr); + if (name is "CrashInExceptionHandler") + { + MethodDescHandle mdHandle = rts.GetMethodDescHandle(methodDescPtr); + TargetCodePointer nativeCode = rts.GetNativeCode(mdHandle); + CodeBlockHandle? handle = executionManager.GetCodeBlockHandle(nativeCode); + Assert.NotNull(handle); + + return handle.Value; + } + } + + Assert.Fail("Could not find CrashInExceptionHandler on the crashing thread's stack"); + return default; + } + + [ConditionalTheory] + [MemberData(nameof(TestConfigurations))] + [SkipOnVersion("net10.0", "EH clause enumeration was added after net10.0")] + public void GetExceptionClauses_ReturnsNonEmptyList(TestConfiguration config) + { + InitializeDumpTest(config); + + CodeBlockHandle codeBlock = FindCrashMethodCodeBlock(); + List clauses = Target.Contracts.ExecutionManager.GetExceptionClauses(codeBlock); + + Assert.True(clauses.Count > 0, "Expected at least one exception clause"); + } + + [ConditionalTheory] + [MemberData(nameof(TestConfigurations))] + [SkipOnVersion("net10.0", "EH clause enumeration was added after net10.0")] + public void GetExceptionClauses_ContainsFilterClause(TestConfiguration config) + { + InitializeDumpTest(config); + + CodeBlockHandle codeBlock = FindCrashMethodCodeBlock(); + List clauses = Target.Contracts.ExecutionManager.GetExceptionClauses(codeBlock); + + Assert.Contains(clauses, c => c.ClauseType == ExceptionClauseInfo.ExceptionClauseFlags.Filter); + } + + [ConditionalTheory] + [MemberData(nameof(TestConfigurations))] + [SkipOnVersion("net10.0", "EH clause enumeration was added after net10.0")] + public void GetExceptionClauses_ContainsTypedClause(TestConfiguration config) + { + InitializeDumpTest(config); + + CodeBlockHandle codeBlock = FindCrashMethodCodeBlock(); + List clauses = Target.Contracts.ExecutionManager.GetExceptionClauses(codeBlock); + + Assert.Contains(clauses, c => c.ClauseType == ExceptionClauseInfo.ExceptionClauseFlags.Typed); + } + + [ConditionalTheory] + [MemberData(nameof(TestConfigurations))] + [SkipOnVersion("net10.0", "EH clause enumeration was added after net10.0")] + public void GetExceptionClauses_ContainsFinallyClause(TestConfiguration config) + { + InitializeDumpTest(config); + + CodeBlockHandle codeBlock = FindCrashMethodCodeBlock(); + List clauses = Target.Contracts.ExecutionManager.GetExceptionClauses(codeBlock); + + Assert.Contains(clauses, c => c.ClauseType == ExceptionClauseInfo.ExceptionClauseFlags.Finally); + } + + [ConditionalTheory] + [MemberData(nameof(TestConfigurations))] + [SkipOnVersion("net10.0", "EH clause enumeration was added after net10.0")] + public void GetExceptionClauses_ContainsCatchAllClause(TestConfiguration config) + { + InitializeDumpTest(config); + + CodeBlockHandle codeBlock = FindCrashMethodCodeBlock(); + List clauses = Target.Contracts.ExecutionManager.GetExceptionClauses(codeBlock); + + Assert.Contains(clauses, c => c.IsCatchAllHandler == true); + } + + [ConditionalTheory] + [MemberData(nameof(TestConfigurations))] + [SkipOnVersion("net10.0", "EH clause enumeration was added after net10.0")] + public void GetExceptionClauses_AllClausesHaveValidOffsets(TestConfiguration config) + { + InitializeDumpTest(config); + + CodeBlockHandle codeBlock = FindCrashMethodCodeBlock(); + List clauses = Target.Contracts.ExecutionManager.GetExceptionClauses(codeBlock); + + foreach (ExceptionClauseInfo clause in clauses) + { + Assert.True(clause.TryStartPC < clause.TryEndPC, + $"TryStartPC (0x{clause.TryStartPC:x}) should be less than TryEndPC (0x{clause.TryEndPC:x})"); + Assert.True(clause.HandlerStartPC < clause.HandlerEndPC, + $"HandlerStartPC (0x{clause.HandlerStartPC:x}) should be less than HandlerEndPC (0x{clause.HandlerEndPC:x})"); + } + } + + [ConditionalTheory] + [MemberData(nameof(TestConfigurations))] + [SkipOnVersion("net10.0", "EH clause enumeration was added after net10.0")] + public void GetExceptionClauses_FilterClauseHasFilterOffset(TestConfiguration config) + { + InitializeDumpTest(config); + + CodeBlockHandle codeBlock = FindCrashMethodCodeBlock(); + List clauses = Target.Contracts.ExecutionManager.GetExceptionClauses(codeBlock); + + ExceptionClauseInfo filterClause = clauses.First(c => c.ClauseType == ExceptionClauseInfo.ExceptionClauseFlags.Filter); + Assert.NotNull(filterClause.FilterOffset); + Assert.Null(filterClause.ClassToken); + Assert.Null(filterClause.TypeHandle); + } + + [ConditionalTheory] + [MemberData(nameof(TestConfigurations))] + [SkipOnVersion("net10.0", "EH clause enumeration was added after net10.0")] + public void GetExceptionClauses_TypedClauseHasModuleAddr(TestConfiguration config) + { + InitializeDumpTest(config); + + CodeBlockHandle codeBlock = FindCrashMethodCodeBlock(); + List clauses = Target.Contracts.ExecutionManager.GetExceptionClauses(codeBlock); + + ExceptionClauseInfo typedClause = clauses.First(c => c.ClauseType == ExceptionClauseInfo.ExceptionClauseFlags.Typed); + Assert.NotNull(typedClause.ModuleAddr); + Assert.NotEqual(TargetPointer.Null, typedClause.ModuleAddr.Value); + Assert.NotNull(typedClause.ClassToken); + } +} diff --git a/src/native/managed/cdac/tests/DumpTests/IXCLRDataAppDomainDumpTests.cs b/src/native/managed/cdac/tests/DumpTests/IXCLRDataAppDomainDumpTests.cs new file mode 100644 index 00000000000000..a4e01f3b5f1100 --- /dev/null +++ b/src/native/managed/cdac/tests/DumpTests/IXCLRDataAppDomainDumpTests.cs @@ -0,0 +1,218 @@ +// 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 Microsoft.Diagnostics.DataContractReader.Contracts; +using Microsoft.Diagnostics.DataContractReader.Legacy; +using Xunit; +using static Microsoft.Diagnostics.DataContractReader.Tests.TestHelpers; + +namespace Microsoft.Diagnostics.DataContractReader.DumpTests; + +/// +/// Dump-based integration tests for IXCLRDataAppDomain methods. +/// Obtains an AppDomain via ClrDataFrame.GetAppDomain from the StackWalk dump. +/// +public unsafe class IXCLRDataAppDomainDumpTests : DumpTestBase +{ + protected override string DebuggeeName => "StackWalk"; + protected override string DumpType => "full"; + + // ========== GetName ========== + + [ConditionalTheory] + [MemberData(nameof(TestConfigurations))] + [SkipOnVersion("net10.0", "InlinedCallFrame.Datum was added after net10.0")] + public void GetName_QueryLengthOnly_ReturnsSizeWithoutBuffer(TestConfiguration config) + { + InitializeDumpTest(config); + IXCLRDataAppDomain appDomain = GetAppDomain(); + + uint nameLen; + int hr = appDomain.GetName(0, &nameLen, null); + + AssertHResult(HResults.S_OK, hr); + Assert.True(nameLen > 1, "Expected nameLen > 1 (name + null terminator)"); + } + + [ConditionalTheory] + [MemberData(nameof(TestConfigurations))] + [SkipOnVersion("net10.0", "InlinedCallFrame.Datum was added after net10.0")] + public void GetName_FullRetrieval_ReturnsNonEmptyName(TestConfiguration config) + { + InitializeDumpTest(config); + IXCLRDataAppDomain appDomain = GetAppDomain(); + + uint nameLen; + int hr = appDomain.GetName(0, &nameLen, null); + AssertHResult(HResults.S_OK, hr); + Assert.True(nameLen <= 1024, "AppDomain name unexpectedly long"); + + char[] nameBuf = new char[nameLen]; + uint nameLen2; + fixed (char* pName = nameBuf) + { + hr = appDomain.GetName(nameLen, &nameLen2, pName); + } + + AssertHResult(HResults.S_OK, hr); + string name = new string(nameBuf, 0, (int)nameLen2 - 1); + Assert.False(string.IsNullOrEmpty(name), "Expected non-empty AppDomain name"); + } + + [ConditionalTheory] + [MemberData(nameof(TestConfigurations))] + [SkipOnVersion("net10.0", "InlinedCallFrame.Datum was added after net10.0")] + public void GetName_NameIsNullTerminated(TestConfiguration config) + { + InitializeDumpTest(config); + IXCLRDataAppDomain appDomain = GetAppDomain(); + + uint nameLen; + int hr = appDomain.GetName(0, &nameLen, null); + AssertHResult(HResults.S_OK, hr); + + char[] nameBuf = new char[nameLen]; + fixed (char* pName = nameBuf) + { + hr = appDomain.GetName(nameLen, null, pName); + AssertHResult(HResults.S_OK, hr); + Assert.Equal('\0', pName[nameLen - 1]); + } + } + + [ConditionalTheory] + [MemberData(nameof(TestConfigurations))] + [SkipOnVersion("net10.0", "InlinedCallFrame.Datum was added after net10.0")] + public void GetName_TruncatedBuffer_ReturnsSFalse(TestConfiguration config) + { + InitializeDumpTest(config); + IXCLRDataAppDomain appDomain = GetAppDomain(); + + uint fullLen; + int hr = appDomain.GetName(0, &fullLen, null); + AssertHResult(HResults.S_OK, hr); + Assert.True(fullLen > 2, "Need a name long enough to truncate"); + + uint truncLen = fullLen - 1; + char[] nameBuf = new char[truncLen]; + uint reportedLen; + fixed (char* pName = nameBuf) + { + hr = appDomain.GetName(truncLen, &reportedLen, pName); + AssertHResult(HResults.S_FALSE, hr); + } + + // neededLen is always the full size, even on truncation. + Assert.Equal(fullLen, reportedLen); + } + + [ConditionalTheory] + [MemberData(nameof(TestConfigurations))] + [SkipOnVersion("net10.0", "InlinedCallFrame.Datum was added after net10.0")] + public void GetName_TruncatedOutput_IsStillNullTerminated(TestConfiguration config) + { + InitializeDumpTest(config); + IXCLRDataAppDomain appDomain = GetAppDomain(); + + uint fullLen; + int hr = appDomain.GetName(0, &fullLen, null); + AssertHResult(HResults.S_OK, hr); + Assert.True(fullLen > 2, "Need a name long enough to truncate"); + + uint truncLen = fullLen - 1; + char[] nameBuf = new char[truncLen]; + fixed (char* pName = nameBuf) + { + appDomain.GetName(truncLen, null, pName); + Assert.Equal('\0', pName[truncLen - 1]); + } + } + + [ConditionalTheory] + [MemberData(nameof(TestConfigurations))] + [SkipOnVersion("net10.0", "InlinedCallFrame.Datum was added after net10.0")] + public void GetName_NeededLenConsistentAcrossCalls(TestConfiguration config) + { + InitializeDumpTest(config); + IXCLRDataAppDomain appDomain = GetAppDomain(); + + uint len1; + int hr = appDomain.GetName(0, &len1, null); + AssertHResult(HResults.S_OK, hr); + + uint len2; + char[] nameBuf = new char[len1]; + fixed (char* pName = nameBuf) + { + hr = appDomain.GetName(len1, &len2, pName); + AssertHResult(HResults.S_OK, hr); + } + + Assert.Equal(len1, len2); + } + + // ========== GetUniqueID ========== + + [ConditionalTheory] + [MemberData(nameof(TestConfigurations))] + [SkipOnVersion("net10.0", "InlinedCallFrame.Datum was added after net10.0")] + public void GetUniqueID_ReturnsOne(TestConfiguration config) + { + InitializeDumpTest(config); + IXCLRDataAppDomain appDomain = GetAppDomain(); + + ulong id; + int hr = appDomain.GetUniqueID(&id); + + AssertHResult(HResults.S_OK, hr); + Assert.Equal(1ul, id); + } + + // ========== GetFlags ========== + + [ConditionalTheory] + [MemberData(nameof(TestConfigurations))] + [SkipOnVersion("net10.0", "InlinedCallFrame.Datum was added after net10.0")] + public void GetFlags_ReturnsDefaultZero(TestConfiguration config) + { + InitializeDumpTest(config); + IXCLRDataAppDomain appDomain = GetAppDomain(); + + uint flags; + int hr = appDomain.GetFlags(&flags); + + AssertHResult(HResults.S_OK, hr); + Assert.Equal(0u, flags); + } + + // ========== Helpers ========== + + private IXCLRDataAppDomain GetAppDomain() + { + IStackWalk stackWalk = Target.Contracts.StackWalk; + ThreadData crashingThread = DumpTestHelpers.FindFailFastThread(Target); + + IStackDataFrameHandle? managedFrame = null; + foreach (IStackDataFrameHandle dataFrame in stackWalk.CreateStackWalk(crashingThread)) + { + TargetPointer md = stackWalk.GetMethodDescPtr(dataFrame); + if (md != TargetPointer.Null) + { + managedFrame = dataFrame; + break; + } + } + + Assert.NotNull(managedFrame); + + ClrDataFrame frame = new ClrDataFrame(Target, managedFrame, legacyImpl: null); + IXCLRDataFrame xclrFrame = frame; + + DacComNullableByRef appDomainOut = new(isNullRef: false); + int hr = xclrFrame.GetAppDomain(appDomainOut); + AssertHResult(HResults.S_OK, hr); + + return appDomainOut.Interface!; + } +} diff --git a/src/native/managed/cdac/tests/DumpTests/IXCLRDataFrameDumpTests.cs b/src/native/managed/cdac/tests/DumpTests/IXCLRDataFrameDumpTests.cs new file mode 100644 index 00000000000000..2784fdab72f8d4 --- /dev/null +++ b/src/native/managed/cdac/tests/DumpTests/IXCLRDataFrameDumpTests.cs @@ -0,0 +1,308 @@ +// 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.Linq; +using Microsoft.Diagnostics.DataContractReader.Contracts; +using Microsoft.Diagnostics.DataContractReader.Legacy; +using Xunit; +using static Microsoft.Diagnostics.DataContractReader.Tests.TestHelpers; + +namespace Microsoft.Diagnostics.DataContractReader.DumpTests; + +/// +/// Dump-based integration tests for IXCLRDataFrame methods. +/// +public unsafe class IXCLRDataFrameDumpTests : DumpTestBase +{ + protected override string DebuggeeName => "StackWalk"; + protected override string DumpType => "full"; + + // ========== GetContext ========== + + [ConditionalTheory] + [MemberData(nameof(TestConfigurations))] + [SkipOnVersion("net10.0", "InlinedCallFrame.Datum was added after net10.0")] + public void GetContext_ReturnsNonEmptyContext(TestConfiguration config) + { + InitializeDumpTest(config); + IXCLRDataFrame frame = CreateFrameForFirstManagedFrame(); + + byte[] contextBuf = new byte[4096]; + uint contextSize; + int hr = frame.GetContext(0, (uint)contextBuf.Length, &contextSize, contextBuf); + + AssertHResult(HResults.S_OK, hr); + Assert.True(contextSize > 0, "Expected non-zero context size"); + Assert.True(contextBuf.Take((int)contextSize).Any(b => b != 0), "Expected non-zero context bytes"); + } + + [ConditionalTheory] + [MemberData(nameof(TestConfigurations))] + [SkipOnVersion("net10.0", "InlinedCallFrame.Datum was added after net10.0")] + public void GetContext_ContextSizeMatchesRawContext(TestConfiguration config) + { + InitializeDumpTest(config); + IStackDataFrameHandle dataFrame = GetFirstManagedFrame(); + IXCLRDataFrame frame = new ClrDataFrame(Target, dataFrame, legacyImpl: null); + + byte[] contextBuf = new byte[4096]; + uint contextSize; + int hr = frame.GetContext(0, (uint)contextBuf.Length, &contextSize, contextBuf); + + AssertHResult(HResults.S_OK, hr); + + byte[] rawContext = Target.Contracts.StackWalk.GetRawContext(dataFrame); + Assert.Equal((uint)rawContext.Length, contextSize); + } + + [ConditionalTheory] + [MemberData(nameof(TestConfigurations))] + [SkipOnVersion("net10.0", "InlinedCallFrame.Datum was added after net10.0")] + public void GetContext_ExactSizeBuffer_CopiesAllBytes(TestConfiguration config) + { + InitializeDumpTest(config); + IStackDataFrameHandle dataFrame = GetFirstManagedFrame(); + IXCLRDataFrame frame = new ClrDataFrame(Target, dataFrame, legacyImpl: null); + + byte[] rawContext = Target.Contracts.StackWalk.GetRawContext(dataFrame); + byte[] contextBuf = new byte[rawContext.Length]; + uint contextSize; + int hr = frame.GetContext(0, (uint)contextBuf.Length, &contextSize, contextBuf); + + AssertHResult(HResults.S_OK, hr); + Assert.Equal((uint)rawContext.Length, contextSize); + Assert.True(contextBuf.SequenceEqual(rawContext), "Context bytes should match raw context exactly"); + } + + [ConditionalTheory] + [MemberData(nameof(TestConfigurations))] + [SkipOnVersion("net10.0", "InlinedCallFrame.Datum was added after net10.0")] + public void GetContext_BufferTooSmall_ReturnsInvalidArg_ButSetsContextSize(TestConfiguration config) + { + InitializeDumpTest(config); + IStackDataFrameHandle dataFrame = GetFirstManagedFrame(); + IXCLRDataFrame frame = new ClrDataFrame(Target, dataFrame, legacyImpl: null); + + byte[] rawContext = Target.Contracts.StackWalk.GetRawContext(dataFrame); + Assert.True(rawContext.Length > 0, "Raw context should not be empty for this test."); + + byte[] tinyBuf = new byte[rawContext.Length - 1]; + uint contextSize; + int hr = frame.GetContext(0, (uint)tinyBuf.Length, &contextSize, tinyBuf); + + AssertHResult(HResults.E_INVALIDARG, hr); + Assert.Equal((uint)rawContext.Length, contextSize); + } + + [ConditionalTheory] + [MemberData(nameof(TestConfigurations))] + [SkipOnVersion("net10.0", "InlinedCallFrame.Datum was added after net10.0")] + public void GetContext_OversizedBuffer_SucceedsWithoutCorruptingExtra(TestConfiguration config) + { + InitializeDumpTest(config); + IStackDataFrameHandle dataFrame = GetFirstManagedFrame(); + IXCLRDataFrame frame = new ClrDataFrame(Target, dataFrame, legacyImpl: null); + + byte[] rawContext = Target.Contracts.StackWalk.GetRawContext(dataFrame); + int oversized = rawContext.Length + 128; + byte[] contextBuf = new byte[oversized]; + // Fill extra region with a sentinel to verify it's untouched. + byte sentinel = 0xAB; + for (int i = rawContext.Length; i < oversized; i++) + contextBuf[i] = sentinel; + + uint contextSize; + int hr = frame.GetContext(0, (uint)oversized, &contextSize, contextBuf); + + AssertHResult(HResults.S_OK, hr); + Assert.Equal((uint)rawContext.Length, contextSize); + Assert.True(contextBuf.AsSpan(0, rawContext.Length).SequenceEqual(rawContext), "Copied bytes should match raw context"); + for (int i = rawContext.Length; i < oversized; i++) + Assert.Equal(sentinel, contextBuf[i]); + } + + // ========== GetAppDomain ========== + + [ConditionalTheory] + [MemberData(nameof(TestConfigurations))] + [SkipOnVersion("net10.0", "InlinedCallFrame.Datum was added after net10.0")] + public void GetAppDomain_ReturnsNonNullAppDomain(TestConfiguration config) + { + InitializeDumpTest(config); + IXCLRDataFrame frame = CreateFrameForFirstManagedFrame(); + + DacComNullableByRef appDomainOut = new(isNullRef: false); + int hr = frame.GetAppDomain(appDomainOut); + + AssertHResult(HResults.S_OK, hr); + Assert.NotNull(appDomainOut.Interface); + } + + [ConditionalTheory] + [MemberData(nameof(TestConfigurations))] + [SkipOnVersion("net10.0", "InlinedCallFrame.Datum was added after net10.0")] + public void GetAppDomain_ReturnedObjectHasNonNullAddress(TestConfiguration config) + { + InitializeDumpTest(config); + IXCLRDataFrame frame = CreateFrameForFirstManagedFrame(); + + DacComNullableByRef appDomainOut = new(isNullRef: false); + int hr = frame.GetAppDomain(appDomainOut); + + AssertHResult(HResults.S_OK, hr); + ClrDataAppDomain appDomain = Assert.IsType(appDomainOut.Interface); + Assert.NotEqual(TargetPointer.Null, appDomain.Address); + } + + [ConditionalTheory] + [MemberData(nameof(TestConfigurations))] + [SkipOnVersion("net10.0", "InlinedCallFrame.Datum was added after net10.0")] + public void GetAppDomain_ConsistentAcrossCalls(TestConfiguration config) + { + InitializeDumpTest(config); + IXCLRDataFrame frame = CreateFrameForFirstManagedFrame(); + + DacComNullableByRef out1 = new(isNullRef: false); + DacComNullableByRef out2 = new(isNullRef: false); + int hr1 = frame.GetAppDomain(out1); + int hr2 = frame.GetAppDomain(out2); + + AssertHResult(HResults.S_OK, hr1); + AssertHResult(HResults.S_OK, hr2); + ClrDataAppDomain ad1 = Assert.IsType(out1.Interface); + ClrDataAppDomain ad2 = Assert.IsType(out2.Interface); + Assert.Equal(ad1.Address, ad2.Address); + } + + // ========== GetNumArguments ========== + + [ConditionalTheory] + [MemberData(nameof(TestConfigurations))] + [SkipOnVersion("net10.0", "InlinedCallFrame.Datum was added after net10.0")] + public void GetNumArguments_ReturnsCountMatchingMetadata(TestConfiguration config) + { + InitializeDumpTest(config); + IStackWalk stackWalk = Target.Contracts.StackWalk; + ThreadData crashingThread = DumpTestHelpers.FindFailFastThread(Target); + + foreach (IStackDataFrameHandle dataFrame in stackWalk.CreateStackWalk(crashingThread)) + { + TargetPointer md = stackWalk.GetMethodDescPtr(dataFrame); + if (md == TargetPointer.Null) + continue; + + string? name = DumpTestHelpers.GetMethodName(Target, md); + if (name is not "MethodA") + continue; + + ClrDataFrame frame = new ClrDataFrame(Target, dataFrame, legacyImpl: null); + IXCLRDataFrame xclrFrame = frame; + uint numArgs; + int hr = xclrFrame.GetNumArguments(&numArgs); + + // MethodA(int depth) is static with 1 parameter → numArgs == 1. + AssertHResult(HResults.S_OK, hr); + Assert.Equal(1u, numArgs); + + return; + } + + Assert.Fail("MethodA not found on the crashing thread's stack"); + } + + // ========== GetNumLocalVariables ========== + + [ConditionalTheory] + [MemberData(nameof(TestConfigurations))] + [SkipOnVersion("net10.0", "InlinedCallFrame.Datum was added after net10.0")] + public void GetNumLocalVariables_ReturnsCountForILMethod(TestConfiguration config) + { + InitializeDumpTest(config); + IStackWalk stackWalk = Target.Contracts.StackWalk; + IRuntimeTypeSystem rts = Target.Contracts.RuntimeTypeSystem; + ThreadData crashingThread = DumpTestHelpers.FindFailFastThread(Target); + + foreach (IStackDataFrameHandle dataFrame in stackWalk.CreateStackWalk(crashingThread)) + { + TargetPointer md = stackWalk.GetMethodDescPtr(dataFrame); + if (md == TargetPointer.Null) + continue; + + string? name = DumpTestHelpers.GetMethodName(Target, md); + if (name is not "MethodB") + continue; + + MethodDescHandle mdh = rts.GetMethodDescHandle(md); + Assert.True(rts.IsIL(mdh), "MethodB should be an IL method"); + + ClrDataFrame frame = new ClrDataFrame(Target, dataFrame, legacyImpl: null); + IXCLRDataFrame xclrFrame = frame; + uint numLocals; + int hr = xclrFrame.GetNumLocalVariables(&numLocals); + + AssertHResult(HResults.S_OK, hr); + Assert.True(numLocals >= 1, $"MethodB should have at least 1 local variable, got {numLocals}"); + + return; + } + + Assert.Fail("MethodB not found on the crashing thread's stack"); + } + + // ========== GetMethodInstance ========== + + [ConditionalTheory] + [MemberData(nameof(TestConfigurations))] + [SkipOnVersion("net10.0", "InlinedCallFrame.Datum was added after net10.0")] + public void GetMethodInstance_ReturnsNonNullForManagedFrame(TestConfiguration config) + { + InitializeDumpTest(config); + IStackWalk stackWalk = Target.Contracts.StackWalk; + ThreadData crashingThread = DumpTestHelpers.FindFailFastThread(Target); + + foreach (IStackDataFrameHandle dataFrame in stackWalk.CreateStackWalk(crashingThread)) + { + TargetPointer md = stackWalk.GetMethodDescPtr(dataFrame); + if (md == TargetPointer.Null) + continue; + + ClrDataFrame frame = new ClrDataFrame(Target, dataFrame, legacyImpl: null); + IXCLRDataFrame xclrFrame = frame; + + DacComNullableByRef methodOut = new(isNullRef: false); + int hr = xclrFrame.GetMethodInstance(methodOut); + + AssertHResult(HResults.S_OK, hr); + Assert.NotNull(methodOut.Interface); + + return; // One check is enough. + } + + Assert.Fail("No managed frames with MethodDesc found"); + } + + // ========== Helpers ========== + + private IStackDataFrameHandle GetFirstManagedFrame() + { + IStackWalk stackWalk = Target.Contracts.StackWalk; + ThreadData crashingThread = DumpTestHelpers.FindFailFastThread(Target); + + foreach (IStackDataFrameHandle dataFrame in stackWalk.CreateStackWalk(crashingThread)) + { + TargetPointer md = stackWalk.GetMethodDescPtr(dataFrame); + if (md != TargetPointer.Null) + return dataFrame; + } + + Assert.Fail("No managed frames with MethodDesc found on the crashing thread's stack"); + throw new InvalidOperationException("Unreachable"); + } + + private IXCLRDataFrame CreateFrameForFirstManagedFrame() + { + IStackDataFrameHandle dataFrame = GetFirstManagedFrame(); + return new ClrDataFrame(Target, dataFrame, legacyImpl: null); + } +} diff --git a/src/native/managed/cdac/tests/DumpTests/RCWInterfacesDumpTests.cs b/src/native/managed/cdac/tests/DumpTests/RCWInterfacesDumpTests.cs new file mode 100644 index 00000000000000..52fb2520eec033 --- /dev/null +++ b/src/native/managed/cdac/tests/DumpTests/RCWInterfacesDumpTests.cs @@ -0,0 +1,75 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Linq; +using Microsoft.Diagnostics.DataContractReader.Contracts; +using Xunit; + +namespace Microsoft.Diagnostics.DataContractReader.DumpTests; + +/// +/// Dump-based integration tests for the BuiltInCOM contract's GetRCWInterfaces API. +/// Uses the RCWInterfaces debuggee which creates a COM RCW and populates the +/// inline interface entry cache before crashing. +/// +public class RCWInterfacesDumpTests : DumpTestBase +{ + protected override string DebuggeeName => "RCWInterfaces"; + protected override string DumpType => "full"; + + [ConditionalTheory] + [MemberData(nameof(TestConfigurations))] + [SkipOnOS(IncludeOnly = "windows", Reason = "COM interop (RCW) is only supported on Windows")] + public void GetRCWInterfaces_FindsRCWAndEnumeratesInterfaces(TestConfiguration config) + { + InitializeDumpTest(config); + IBuiltInCOM builtInCOM = Target.Contracts.BuiltInCOM; + IObject objectContract = Target.Contracts.Object; + IGC gcContract = Target.Contracts.GC; + + Assert.NotNull(builtInCOM); + Assert.NotNull(objectContract); + Assert.NotNull(gcContract); + + // Walk all strong GC handles to find objects with COM data (RCWs) + List strongHandles = gcContract.GetHandles([HandleType.Strong]); + TargetPointer rcwPtr = TargetPointer.Null; + + foreach (HandleData handleData in strongHandles) + { + TargetPointer objectAddress = Target.ReadPointer(handleData.Handle); + if (objectAddress == TargetPointer.Null) + continue; + + if (objectContract.GetBuiltInComData(objectAddress, out TargetPointer rcw, out _, out _) + && rcw != TargetPointer.Null) + { + rcwPtr = rcw; + break; + } + } + + Assert.NotEqual(TargetPointer.Null, rcwPtr); + + // Assert that the cookie is not null + TargetPointer cookie = builtInCOM.GetRCWContext(rcwPtr); + Assert.NotEqual(TargetPointer.Null, cookie); + + // Call GetRCWInterfaces on the found RCW — must not throw + List<(TargetPointer MethodTable, TargetPointer Unknown)> interfaces = + builtInCOM.GetRCWInterfaces(rcwPtr).ToList(); + + // The debuggee interacts with the RCW via IGlobalInterfaceTable / IUnknown, + // so the entry cache should have at least one cached interface entry + Assert.True(interfaces.Count >= 1, + $"Expected at least one cached interface entry in the RCW, got {interfaces.Count}"); + + // Every returned entry must have non-null MethodTable and Unknown pointers + foreach ((TargetPointer mt, TargetPointer unk) in interfaces) + { + Assert.NotEqual(TargetPointer.Null, mt); + Assert.NotEqual(TargetPointer.Null, unk); + } + } +} diff --git a/src/native/managed/cdac/tests/DumpTests/RuntimeInfoDumpTests.cs b/src/native/managed/cdac/tests/DumpTests/RuntimeInfoDumpTests.cs index 695e9c59c60267..7a3517cb285ace 100644 --- a/src/native/managed/cdac/tests/DumpTests/RuntimeInfoDumpTests.cs +++ b/src/native/managed/cdac/tests/DumpTests/RuntimeInfoDumpTests.cs @@ -31,6 +31,8 @@ public void RuntimeInfo_ArchitectureMatchesDumpMetadata(TestConfiguration config "x86" => RuntimeInfoArchitecture.X86, "arm64" => RuntimeInfoArchitecture.Arm64, "arm" => RuntimeInfoArchitecture.Arm, + "riscv64" => RuntimeInfoArchitecture.RiscV64, + "loongarch64" => RuntimeInfoArchitecture.LoongArch64, _ => RuntimeInfoArchitecture.Unknown, }; diff --git a/src/native/managed/cdac/tests/GetRegisterNameTests.cs b/src/native/managed/cdac/tests/GetRegisterNameTests.cs index c993103539be4a..6aed07e9fc72ba 100644 --- a/src/native/managed/cdac/tests/GetRegisterNameTests.cs +++ b/src/native/managed/cdac/tests/GetRegisterNameTests.cs @@ -61,7 +61,7 @@ public static IEnumerable BasicRegisterNameData() yield return [RuntimeInfoArchitecture.LoongArch64, 3, "SP"]; // RiscV64 registers - yield return [RuntimeInfoArchitecture.RiscV64, 0, "R0"]; + yield return [RuntimeInfoArchitecture.RiscV64, 0, "zero"]; yield return [RuntimeInfoArchitecture.RiscV64, 1, "RA"]; yield return [RuntimeInfoArchitecture.RiscV64, 2, "SP"]; } @@ -93,6 +93,8 @@ public void GetRegisterName_ReturnsCorrectName( [InlineData(RuntimeInfoArchitecture.X64, 0, "rax")] [InlineData(RuntimeInfoArchitecture.X86, 3, "ebx")] [InlineData(RuntimeInfoArchitecture.Arm64, 0, "X0")] + [InlineData(RuntimeInfoArchitecture.LoongArch64, 1, "RA")] + [InlineData(RuntimeInfoArchitecture.RiscV64, 1, "RA")] public void GetRegisterName_CallerFrame_PrependsCaller( RuntimeInfoArchitecture targetArch, int regNum, diff --git a/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.ExecutionManager.cs b/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.ExecutionManager.cs index f4edee3e0e4ffd..524ff8e21405dc 100644 --- a/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.ExecutionManager.cs +++ b/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.ExecutionManager.cs @@ -17,7 +17,7 @@ internal class ExecutionManager { public const ulong ExecutionManagerCodeRangeMapAddress = 0x000a_fff0; - const int RealCodeHeaderSize = 0x28; // must be big enough for the offsets of RealCodeHeader size in ExecutionManagerTestTarget, below + const int RealCodeHeaderSize = 0x30; // must be big enough for the offsets of RealCodeHeader size in ExecutionManagerTestTarget, below public struct AllocationRange { @@ -236,6 +236,7 @@ public static RangeSectionMapTestBuilder CreateRangeSection(MockTarget.Architect new(nameof(Data.RealCodeHeader.GCInfo), DataType.pointer), new(nameof(Data.RealCodeHeader.NumUnwindInfos), DataType.uint32), new(nameof(Data.RealCodeHeader.UnwindInfos), DataType.pointer), + new(nameof(Data.RealCodeHeader.JitEHInfo), DataType.pointer), ] }; @@ -253,6 +254,8 @@ public static RangeSectionMapTestBuilder CreateRangeSection(MockTarget.Architect new(nameof(Data.ReadyToRunInfo.DelayLoadMethodCallThunks), DataType.pointer), new(nameof(Data.ReadyToRunInfo.DebugInfoSection), DataType.pointer), new(nameof(Data.ReadyToRunInfo.EntryPointToMethodDescMap), DataType.Unknown, helpers.LayoutFields(MockDescriptors.HashMap.HashMapFields.Fields).Stride), + new(nameof(Data.ReadyToRunInfo.LoadedImageBase), DataType.pointer), + new(nameof(Data.ReadyToRunInfo.Composite), DataType.pointer), ] }; @@ -510,6 +513,7 @@ public TargetCodePointer AddJittedMethod(JittedCodeRange jittedCodeRange, uint c Builder.TargetTestHelpers.WritePointer(chf.Slice(tyInfo.Fields[nameof(Data.RealCodeHeader.GCInfo)].Offset, Builder.TargetTestHelpers.PointerSize), TargetPointer.Null); Builder.TargetTestHelpers.Write(chf.Slice(tyInfo.Fields[nameof(Data.RealCodeHeader.NumUnwindInfos)].Offset, sizeof(uint)), 0u); Builder.TargetTestHelpers.WritePointer(chf.Slice(tyInfo.Fields[nameof(Data.RealCodeHeader.UnwindInfos)].Offset, Builder.TargetTestHelpers.PointerSize), TargetPointer.Null); + Builder.TargetTestHelpers.WritePointer(chf.Slice(tyInfo.Fields[nameof(Data.RealCodeHeader.JitEHInfo)].Offset, Builder.TargetTestHelpers.PointerSize), TargetPointer.Null); return codeStart; } diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj b/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj index b3567e0d4dfa85..c1e48494c32e1e 100644 --- a/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj +++ b/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj @@ -1,13 +1,20 @@ - - $(NetCoreAppToolCurrent) - $(NoWarn),CA1050,CA1850 + $(NetCoreAppToolCurrent);$(NetFrameworkToolCurrent) + enable + + $(NoWarn),CA1050,CS8604,CS8602 + $(NoWarn),CA1050,CA1850 false true true + + + + + @@ -31,17 +38,25 @@ - + + - - + + + + + + - + diff --git a/src/tasks/WasmAppBuilder/coreclr/PInvokeTableGenerator.cs b/src/tasks/WasmAppBuilder/coreclr/PInvokeTableGenerator.cs index 41d13a98405c64..b3d9c43f6cbd51 100644 --- a/src/tasks/WasmAppBuilder/coreclr/PInvokeTableGenerator.cs +++ b/src/tasks/WasmAppBuilder/coreclr/PInvokeTableGenerator.cs @@ -267,7 +267,7 @@ private static string PickCTypeNameForUnknownType(Type t) if (!t.IsValueType) return "void *"; // Pass pointers and function pointers by-value - else if (t.IsPointer || t.IsFunctionPointer) + else if (t.IsPointer || IsFunctionPointer(t)) return "void *"; else if (t.IsPrimitive) throw new NotImplementedException("No native type mapping for type " + t); @@ -504,6 +504,12 @@ private string ThunkMapEntryLine(PInvokeCallback cb, LogAdapter Log) private static readonly Dictionary _blittableCache = new(); + public static bool IsFunctionPointer(Type type) + { + object? bIsFunctionPointer = type.GetType().GetProperty("IsFunctionPointer")?.GetValue(type); + return (bIsFunctionPointer is bool b) && b; + } + public static bool IsBlittable(Type type, LogAdapter log) { // We maintain a cache of results in order to only produce log messages the first time @@ -523,7 +529,7 @@ static bool IsBlittableUncached(Type type, LogAdapter log) if (type.IsPrimitive || type.IsByRef || type.IsPointer || type.IsEnum) return true; - if (type.IsFunctionPointer) + if (IsFunctionPointer(type)) return true; // HACK: SkiaSharp has pinvokes that rely on this diff --git a/src/tasks/WasmAppBuilder/coreclr/SignatureMapper.cs b/src/tasks/WasmAppBuilder/coreclr/SignatureMapper.cs index 69085ff5b17c5b..a5a5bcb2d4993f 100644 --- a/src/tasks/WasmAppBuilder/coreclr/SignatureMapper.cs +++ b/src/tasks/WasmAppBuilder/coreclr/SignatureMapper.cs @@ -63,7 +63,7 @@ internal static class SignatureMapper } else if (t.IsPointer) c = 'i'; - else if (t.IsFunctionPointer) + else if (PInvokeTableGenerator.IsFunctionPointer(t)) c = 'i'; else if (t.IsValueType) { diff --git a/src/tests/Common/GenerateHWIntrinsicTests/Arm/SveTests.cs b/src/tests/Common/GenerateHWIntrinsicTests/Arm/SveTests.cs index a646a4603193e4..a596fac36c5ded 100644 --- a/src/tests/Common/GenerateHWIntrinsicTests/Arm/SveTests.cs +++ b/src/tests/Common/GenerateHWIntrinsicTests/Arm/SveTests.cs @@ -620,26 +620,16 @@ static class SveTests (Templates.SveGatherVector, new Dictionary {["TestName"] = "Sve_GatherVectorUInt16ZeroExtend_Indices_ulong_ulong", ["Method"] = "GatherVectorUInt16ZeroExtend", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "UInt16", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "UInt64", ["ExtendedElementType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt64()", ["AreOffsets"] = "false" }), (Templates.SveGatherVector, new Dictionary {["TestName"] = "Sve_GatherVectorUInt32WithByteOffsetsZeroExtend_Offsets_long_long", ["Method"] = "GatherVectorUInt32WithByteOffsetsZeroExtend", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2BaseType"] = "UInt32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Int64", ["ExtendedElementType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetInt64()", ["AreOffsets"] = "true" }), - (Templates.SveGatherVector, new Dictionary {["TestName"] = "Sve_GatherVectorUInt32WithByteOffsetsZeroExtend_Offsets_int_int", ["Method"] = "GatherVectorUInt32WithByteOffsetsZeroExtend", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2BaseType"] = "UInt32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Int32", ["ExtendedElementType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetInt32()", ["AreOffsets"] = "true" }), (Templates.SveGatherVector, new Dictionary {["TestName"] = "Sve_GatherVectorUInt32WithByteOffsetsZeroExtend_Offsets_ulong_long", ["Method"] = "GatherVectorUInt32WithByteOffsetsZeroExtend", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "UInt32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Int64", ["ExtendedElementType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetInt64()", ["AreOffsets"] = "true" }), - (Templates.SveGatherVector, new Dictionary {["TestName"] = "Sve_GatherVectorUInt32WithByteOffsetsZeroExtend_Offsets_uint_int", ["Method"] = "GatherVectorUInt32WithByteOffsetsZeroExtend", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2BaseType"] = "UInt32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Int32", ["ExtendedElementType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetInt32()", ["AreOffsets"] = "true" }), (Templates.SveGatherVector, new Dictionary {["TestName"] = "Sve_GatherVectorUInt32WithByteOffsetsZeroExtend_Offsets_long_ulong", ["Method"] = "GatherVectorUInt32WithByteOffsetsZeroExtend", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2BaseType"] = "UInt32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "UInt64", ["ExtendedElementType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt64()", ["AreOffsets"] = "true" }), - (Templates.SveGatherVector, new Dictionary {["TestName"] = "Sve_GatherVectorUInt32WithByteOffsetsZeroExtend_Offsets_int_uint", ["Method"] = "GatherVectorUInt32WithByteOffsetsZeroExtend", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2BaseType"] = "UInt32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "UInt32", ["ExtendedElementType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt32()", ["AreOffsets"] = "true" }), (Templates.SveGatherVector, new Dictionary {["TestName"] = "Sve_GatherVectorUInt32WithByteOffsetsZeroExtend_Offsets_ulong_ulong", ["Method"] = "GatherVectorUInt32WithByteOffsetsZeroExtend", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "UInt32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "UInt64", ["ExtendedElementType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt64()", ["AreOffsets"] = "true" }), - (Templates.SveGatherVector, new Dictionary {["TestName"] = "Sve_GatherVectorUInt32WithByteOffsetsZeroExtend_Offsets_uint_uint", ["Method"] = "GatherVectorUInt32WithByteOffsetsZeroExtend", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2BaseType"] = "UInt32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "UInt32", ["ExtendedElementType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt32()", ["AreOffsets"] = "true" }), (Templates.SveGatherVectorVectorBases,new Dictionary {["TestName"] = "Sve_GatherVectorUInt32ZeroExtend_Bases_long_ulong", ["Method"] = "GatherVectorUInt32ZeroExtend", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt64", ["ExtendedElementType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["NextValueBase"] = "TestLibrary.Generator.GetInt64()",}), - // (Templates.SveGatherVectorVectorBases,new Dictionary {["TestName"] = "Sve_GatherVectorUInt32ZeroExtend_Bases_int_uint", ["Method"] = "GatherVectorUInt32ZeroExtend", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt32", ["ExtendedElementType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueBase"] = "TestLibrary.Generator.GetInt32()"}), (Templates.SveGatherVectorVectorBases,new Dictionary {["TestName"] = "Sve_GatherVectorUInt32ZeroExtend_Bases_ulong_ulong", ["Method"] = "GatherVectorUInt32ZeroExtend", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt64", ["ExtendedElementType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["NextValueBase"] = "TestLibrary.Generator.GetUInt64()"}), - // (Templates.SveGatherVectorVectorBases,new Dictionary {["TestName"] = "Sve_GatherVectorUInt32ZeroExtend_Bases_uint_uint", ["Method"] = "GatherVectorUInt32ZeroExtend", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt32", ["ExtendedElementType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueBase"] = "TestLibrary.Generator.GetUInt32()}), (Templates.SveGatherVector, new Dictionary {["TestName"] = "Sve_GatherVectorUInt32ZeroExtend_Indices_long_long", ["Method"] = "GatherVectorUInt32ZeroExtend", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2BaseType"] = "UInt32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Int64", ["ExtendedElementType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetInt64()", ["AreOffsets"] = "false" }), - (Templates.SveGatherVector, new Dictionary {["TestName"] = "Sve_GatherVectorUInt32ZeroExtend_Indices_int_int", ["Method"] = "GatherVectorUInt32ZeroExtend", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2BaseType"] = "UInt32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Int32", ["ExtendedElementType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetInt32()", ["AreOffsets"] = "false" }), (Templates.SveGatherVector, new Dictionary {["TestName"] = "Sve_GatherVectorUInt32ZeroExtend_Indices_ulong_long", ["Method"] = "GatherVectorUInt32ZeroExtend", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "UInt32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Int64", ["ExtendedElementType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetInt64()", ["AreOffsets"] = "false" }), - (Templates.SveGatherVector, new Dictionary {["TestName"] = "Sve_GatherVectorUInt32ZeroExtend_Indices_uint_int", ["Method"] = "GatherVectorUInt32ZeroExtend", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2BaseType"] = "UInt32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Int32", ["ExtendedElementType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetInt32()", ["AreOffsets"] = "false" }), (Templates.SveGatherVector, new Dictionary {["TestName"] = "Sve_GatherVectorUInt32ZeroExtend_Indices_long_ulong", ["Method"] = "GatherVectorUInt32ZeroExtend", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2BaseType"] = "UInt32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "UInt64", ["ExtendedElementType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt64()", ["AreOffsets"] = "false" }), - (Templates.SveGatherVector, new Dictionary {["TestName"] = "Sve_GatherVectorUInt32ZeroExtend_Indices_int_uint", ["Method"] = "GatherVectorUInt32ZeroExtend", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2BaseType"] = "UInt32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "UInt32", ["ExtendedElementType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt32()", ["AreOffsets"] = "false" }), (Templates.SveGatherVector, new Dictionary {["TestName"] = "Sve_GatherVectorUInt32ZeroExtend_Indices_ulong_ulong", ["Method"] = "GatherVectorUInt32ZeroExtend", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "UInt32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "UInt64", ["ExtendedElementType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt64()", ["AreOffsets"] = "false" }), - (Templates.SveGatherVector, new Dictionary {["TestName"] = "Sve_GatherVectorUInt32ZeroExtend_Indices_uint_uint", ["Method"] = "GatherVectorUInt32ZeroExtend", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2BaseType"] = "UInt32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "UInt32", ["ExtendedElementType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt32()", ["AreOffsets"] = "false" }), (Templates.SveGatherVectorFirstFaultingVectorBases, new Dictionary {["TestName"] = "Sve_GatherVectorFirstFaulting_Bases_double_ulong", ["Method"] = "GatherVectorFirstFaulting", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt64", ["ExtendedElementType"] = "Double", ["GetFfrType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskDouble()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["NextValueBase"] = "TestLibrary.Generator.GetDouble()"}), (Templates.SveGatherVectorFirstFaultingVectorBases, new Dictionary {["TestName"] = "Sve_GatherVectorFirstFaulting_Bases_long_ulong", ["Method"] = "GatherVectorFirstFaulting", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt64", ["ExtendedElementType"] = "Int64", ["GetFfrType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["NextValueBase"] = "TestLibrary.Generator.GetInt64()"}), @@ -714,20 +704,12 @@ static class SveTests (Templates.SveGatherVectorFirstFaulting, new Dictionary { ["TestName"] = "Sve_GatherVectorUInt16ZeroExtendFirstFaulting_Indices_uint_uint", ["Method"] = "GatherVectorUInt16ZeroExtendFirstFaulting", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2BaseType"] = "UInt16", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "UInt32", ["ExtendedElementType"] = "UInt16", ["GetFfrType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt32()", ["AreOffsets"] = "false" }), (Templates.SveGatherVectorFirstFaulting, new Dictionary { ["TestName"] = "Sve_GatherVectorUInt16ZeroExtendFirstFaulting_Indices_ulong_long", ["Method"] = "GatherVectorUInt16ZeroExtendFirstFaulting", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "UInt16", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Int64", ["ExtendedElementType"] = "UInt16", ["GetFfrType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["NextValueOp3"] = "TestLibrary.Generator.GetInt64()", ["AreOffsets"] = "false" }), (Templates.SveGatherVectorFirstFaulting, new Dictionary { ["TestName"] = "Sve_GatherVectorUInt16ZeroExtendFirstFaulting_Indices_ulong_ulong", ["Method"] = "GatherVectorUInt16ZeroExtendFirstFaulting", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "UInt16", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "UInt64", ["ExtendedElementType"] = "UInt16", ["GetFfrType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt64()", ["AreOffsets"] = "false" }), - (Templates.SveGatherVectorByteOffsetFirstFaulting, new Dictionary { ["TestName"] = "Sve_GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting_offsets_int_int", ["Method"] = "GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2BaseType"] = "UInt32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Int32", ["GetFfrType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetInt32()", ["ConvertFunc"] = "(UInt32)"}), - (Templates.SveGatherVectorByteOffsetFirstFaulting, new Dictionary { ["TestName"] = "Sve_GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting_offsets_int_uint", ["Method"] = "GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2BaseType"] = "UInt32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "UInt32", ["GetFfrType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt32()", ["ConvertFunc"] = "(UInt32)"}), (Templates.SveGatherVectorByteOffsetFirstFaulting, new Dictionary { ["TestName"] = "Sve_GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting_offsets_long_long", ["Method"] = "GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2BaseType"] = "UInt32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Int64", ["GetFfrType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetInt64()", ["ConvertFunc"] = "(UInt32)"}), (Templates.SveGatherVectorByteOffsetFirstFaulting, new Dictionary { ["TestName"] = "Sve_GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting_offsets_long_ulong", ["Method"] = "GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2BaseType"] = "UInt32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "UInt64", ["GetFfrType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt64()", ["ConvertFunc"] = "(UInt32)"}), - (Templates.SveGatherVectorByteOffsetFirstFaulting, new Dictionary { ["TestName"] = "Sve_GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting_offsets_uint_int", ["Method"] = "GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2BaseType"] = "UInt32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Int32", ["GetFfrType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetInt32()", ["ConvertFunc"] = "(UInt32)"}), - (Templates.SveGatherVectorByteOffsetFirstFaulting, new Dictionary { ["TestName"] = "Sve_GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting_offsets_uint_uint", ["Method"] = "GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2BaseType"] = "UInt32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "UInt32", ["GetFfrType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt32()", ["ConvertFunc"] = "(UInt32)"}), (Templates.SveGatherVectorByteOffsetFirstFaulting, new Dictionary { ["TestName"] = "Sve_GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting_offsets_ulong_long", ["Method"] = "GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "UInt32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Int64", ["GetFfrType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetInt64()", ["ConvertFunc"] = "(UInt32)"}), (Templates.SveGatherVectorByteOffsetFirstFaulting, new Dictionary { ["TestName"] = "Sve_GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting_offsets_ulong_ulong", ["Method"] = "GatherVectorUInt32WithByteOffsetsZeroExtendFirstFaulting", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "UInt32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "UInt64", ["GetFfrType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt64()", ["ConvertFunc"] = "(UInt32)"}), - (Templates.SveGatherVectorFirstFaulting, new Dictionary { ["TestName"] = "Sve_GatherVectorUInt32ZeroExtendFirstFaulting_Indices_int_int", ["Method"] = "GatherVectorUInt32ZeroExtendFirstFaulting", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2BaseType"] = "UInt32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Int32", ["ExtendedElementType"] = "UInt32", ["GetFfrType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetInt32()", ["AreOffsets"] = "false" }), - (Templates.SveGatherVectorFirstFaulting, new Dictionary { ["TestName"] = "Sve_GatherVectorUInt32ZeroExtendFirstFaulting_Indices_int_uint", ["Method"] = "GatherVectorUInt32ZeroExtendFirstFaulting", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2BaseType"] = "UInt32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "UInt32", ["ExtendedElementType"] = "UInt32", ["GetFfrType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt32()", ["AreOffsets"] = "false" }), (Templates.SveGatherVectorFirstFaulting, new Dictionary { ["TestName"] = "Sve_GatherVectorUInt32ZeroExtendFirstFaulting_Indices_long_long", ["Method"] = "GatherVectorUInt32ZeroExtendFirstFaulting", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2BaseType"] = "UInt32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Int64", ["ExtendedElementType"] = "UInt32", ["GetFfrType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetInt64()", ["AreOffsets"] = "false" }), (Templates.SveGatherVectorFirstFaulting, new Dictionary { ["TestName"] = "Sve_GatherVectorUInt32ZeroExtendFirstFaulting_Indices_long_ulong", ["Method"] = "GatherVectorUInt32ZeroExtendFirstFaulting", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2BaseType"] = "UInt32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "UInt64", ["ExtendedElementType"] = "UInt32", ["GetFfrType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt64()", ["AreOffsets"] = "false" }), - (Templates.SveGatherVectorFirstFaulting, new Dictionary { ["TestName"] = "Sve_GatherVectorUInt32ZeroExtendFirstFaulting_Indices_uint_int", ["Method"] = "GatherVectorUInt32ZeroExtendFirstFaulting", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2BaseType"] = "UInt32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Int32", ["ExtendedElementType"] = "UInt32", ["GetFfrType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetInt32()", ["AreOffsets"] = "false" }), - (Templates.SveGatherVectorFirstFaulting, new Dictionary { ["TestName"] = "Sve_GatherVectorUInt32ZeroExtendFirstFaulting_Indices_uint_uint", ["Method"] = "GatherVectorUInt32ZeroExtendFirstFaulting", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2BaseType"] = "UInt32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "UInt32", ["ExtendedElementType"] = "UInt32", ["GetFfrType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt32()", ["AreOffsets"] = "false" }), (Templates.SveGatherVectorFirstFaulting, new Dictionary { ["TestName"] = "Sve_GatherVectorUInt32ZeroExtendFirstFaulting_Indices_ulong_long", ["Method"] = "GatherVectorUInt32ZeroExtendFirstFaulting", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "UInt32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Int64", ["ExtendedElementType"] = "UInt32", ["GetFfrType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetInt64()", ["AreOffsets"] = "false" }), (Templates.SveGatherVectorFirstFaulting, new Dictionary { ["TestName"] = "Sve_GatherVectorUInt32ZeroExtendFirstFaulting_Indices_ulong_ulong", ["Method"] = "GatherVectorUInt32ZeroExtendFirstFaulting", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "UInt32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "UInt64", ["ExtendedElementType"] = "UInt32", ["GetFfrType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "Helpers.getMaskUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt64()", ["AreOffsets"] = "false" }), diff --git a/src/tests/Directory.Build.props b/src/tests/Directory.Build.props index 960236555d9533..df43d981422b54 100644 --- a/src/tests/Directory.Build.props +++ b/src/tests/Directory.Build.props @@ -139,7 +139,7 @@ xUnit1028: Use supported test return types. We support test methods that return int where 100 is passing and everything else is failure. xUnit1031: Don't use task blocking operations. Our runner doesn't set up a synchronization context, so we can use the blocking operations without issue. --> - $(NoWarn);xUnit1028;xUnit1031 + $(NoWarn);xUnit1028;xUnit1031;xUnit1051 false false false diff --git a/src/tests/GC/Regressions/Github/Runtime_76219/Runtime_76219.csproj b/src/tests/GC/Regressions/Github/Runtime_76219/Runtime_76219.csproj index aefac61c542f2b..657d4ca65cbad7 100644 --- a/src/tests/GC/Regressions/Github/Runtime_76219/Runtime_76219.csproj +++ b/src/tests/GC/Regressions/Github/Runtime_76219/Runtime_76219.csproj @@ -5,6 +5,9 @@ True 1 + + + true diff --git a/src/tests/JIT/Methodical/Boxing/morph/sin3double.il b/src/tests/JIT/Methodical/Boxing/morph/sin3double.il index aaf4fd9619f17f..dbe6d04d3707bd 100644 --- a/src/tests/JIT/Methodical/Boxing/morph/sin3double.il +++ b/src/tests/JIT/Methodical/Boxing/morph/sin3double.il @@ -14,7 +14,6 @@ } .assembly extern xunit.core {} .assembly extern Microsoft.DotNet.XUnitExtensions { .publickeytoken = (31 BF 38 56 AD 36 4E 35 ) } -.assembly extern TestLibrary { .ver 0:0:0:0 } .namespace Test_sin3double { .class private sequential ansi sealed beforefieldinit VV extends [mscorlib]System.ValueType @@ -204,11 +203,6 @@ 01 00 00 00 ) .custom instance void [Microsoft.DotNet.XUnitExtensions]Xunit.ActiveIssueAttribute::.ctor(string, valuetype [Microsoft.DotNet.XUnitExtensions]Xunit.TestRuntimes) = {string('https://github.com/dotnet/runtime/issues/34196') int32(2)} - .custom instance void [Microsoft.DotNet.XUnitExtensions]Xunit.ActiveIssueAttribute::.ctor(string, class [mscorlib]System.Type, string[]) = { - string('https://github.com/dotnet/runtime/issues/124222') - type([TestLibrary]TestLibrary.PlatformDetection) - string[1] ('IsWasm') - } .entrypoint .locals (int32 V_0, float64 V_1, diff --git a/src/tests/JIT/Methodical/flowgraph/bug619534/moduleHandleCache.cs b/src/tests/JIT/Methodical/flowgraph/bug619534/moduleHandleCache.cs index 29ad67b52dc838..207cc2b205c0e3 100644 --- a/src/tests/JIT/Methodical/flowgraph/bug619534/moduleHandleCache.cs +++ b/src/tests/JIT/Methodical/flowgraph/bug619534/moduleHandleCache.cs @@ -23,7 +23,6 @@ at Repro.Main() in c:\tests\Dev10\640711\app.cs:line 16 using System; using System.Runtime.CompilerServices; -using TestLibrary; using Xunit; namespace Test_moduleHandleCache_cs @@ -42,7 +41,6 @@ static void Caller(bool b) } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/124222", typeof(PlatformDetection), nameof(PlatformDetection.IsWasm))] public static void TestEntryPoint() { try diff --git a/src/tests/JIT/Methodical/switch/switch6.il b/src/tests/JIT/Methodical/switch/switch6.il index 89bfeed2aa1818..898b185fb4152b 100644 --- a/src/tests/JIT/Methodical/switch/switch6.il +++ b/src/tests/JIT/Methodical/switch/switch6.il @@ -6,8 +6,6 @@ .assembly extern System.Runtime.Extensions { auto } .assembly extern legacy library mscorlib { auto } .assembly extern System.Console { auto } -.assembly extern Microsoft.DotNet.XUnitExtensions { .publickeytoken = (31 BF 38 56 AD 36 4E 35 ) } -.assembly extern TestLibrary { .ver 0:0:0:0 } .assembly 'switch6' { @@ -89,11 +87,6 @@ .custom instance void [xunit.core]Xunit.FactAttribute::.ctor() = ( 01 00 00 00 ) - .custom instance void [Microsoft.DotNet.XUnitExtensions]Xunit.ActiveIssueAttribute::.ctor(string, class [mscorlib]System.Type, string[]) = { - string('https://github.com/dotnet/runtime/issues/124222') - type([TestLibrary]TestLibrary.PlatformDetection) - string[1] ('IsWasm') - } .entrypoint .maxstack 8 IL_0000: ldc.i4.1 diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_125301/Runtime_125301.cs b/src/tests/JIT/Regression/JitBlue/Runtime_125301/Runtime_125301.cs new file mode 100644 index 00000000000000..88632eea6ab44e --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_125301/Runtime_125301.cs @@ -0,0 +1,34 @@ +// 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.CompilerServices; +using Xunit; + +public class Runtime_125301 +{ + [Fact] + public static void TestEntryPoint() + { + const short expected = 6; + short swapped = Swap(expected); + short result = Swap(swapped); + + Assert.Equal(expected, result); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static unsafe short Swap(short value) + { + short returnValue = short.MinValue; + + Swap2((byte*)&value, (byte*)&returnValue); + + return returnValue; + } + + private static unsafe void Swap2(byte* originalBytes, byte* returnBytes) + { + returnBytes[0] = originalBytes[1]; + returnBytes[1] = originalBytes[0]; + } +} diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_125327/Runtime_125327.cs b/src/tests/JIT/Regression/JitBlue/Runtime_125327/Runtime_125327.cs new file mode 100644 index 00000000000000..1fd0b4494d3abc --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_125327/Runtime_125327.cs @@ -0,0 +1,66 @@ +// Generated by Fuzzlyn v3.3 on 2026-03-08 16:41:49 +// Run on Arm64 MacOS +// Seed: 9286072369653102690-vectort,vector64,vector128,armadvsimd,armadvsimdarm64,armaes,armarmbase,armarmbasearm64,armcrc32,armcrc32arm64,armdp,armrdm,armrdmarm64,armsha1,armsha256 +// Reduced from 299.3 KiB to 0.3 KiB in 00:02:44 +// Hits JIT assert for Release: +// Assertion failed 'false && "found use of a node that is not in the LIR sequence"' in 'Program:M4():int' during 'Lowering nodeinfo' (IL size 30; hash 0xadbe37fa; FullOpts) +// +// File: /Users/runner/work/1/s/src/coreclr/jit/lir.cpp Line: 1686 +// +// Generated by Fuzzlyn v3.3 on 2026-03-08 16:30:55 +// Run on Arm64 MacOS +// Seed: 10562635396882260649-vectort,vector64,vector128,armadvsimd,armadvsimdarm64,armaes,armarmbase,armarmbasearm64,armcrc32,armcrc32arm64,armdp,armrdm,armrdmarm64,armsha1,armsha256 +// Reduced from 120.5 KiB to 0.4 KiB in 00:01:43 +// Hits JIT assert for Release: +// Assertion failed '!HAS_FIXED_REGISTER_SET || arg->OperIsPutArg()' in 'Program:M9(short):int' during 'Lowering nodeinfo' (IL size 52; hash 0x4c4f6f25; FullOpts) +// +// File: /Users/runner/work/1/s/src/coreclr/jit/lower.cpp Line: 9406 +// + +using System; +using System.Runtime.CompilerServices; +using Xunit; + +public class Runtime_125327 +{ + public static long[] s_1 = new long[] + { + 0 + }; + + [Fact] + public static void TestEntryPoint1() + { + bool vr5 = M4() == s_1[0]; + // Assert.Equal(M4(), s_1[0]); + } + + public static int M4() + { + if ((ushort)(s_1[0] & 3245280089U) > -1L) + { + var vr2 = s_1[0]; + } + + return 1; + } + + public static short s_6; + + [Fact] + public static void TestEntryPoint2() + { + M9(s_6); + } + + public static int M9(short arg0) + { + ushort[] var1 = new ushort[] + { + 1 + }; + bool var2 = (0 >> var1[0]) <= (long)(s_6 & 1); + System.Console.WriteLine(var2); + return 1; + } +} diff --git a/src/tests/JIT/Regression/Regression_ro_1.csproj b/src/tests/JIT/Regression/Regression_ro_1.csproj index ae4e3c12a3594f..b332ffd68aeac6 100644 --- a/src/tests/JIT/Regression/Regression_ro_1.csproj +++ b/src/tests/JIT/Regression/Regression_ro_1.csproj @@ -81,6 +81,7 @@ + diff --git a/src/tests/JIT/Regression/Regression_ro_2.csproj b/src/tests/JIT/Regression/Regression_ro_2.csproj index e8691408273318..62a1b17f16ef53 100644 --- a/src/tests/JIT/Regression/Regression_ro_2.csproj +++ b/src/tests/JIT/Regression/Regression_ro_2.csproj @@ -91,6 +91,7 @@ + diff --git a/src/tests/JIT/opt/InstructionCombining/Casts.cs b/src/tests/JIT/opt/InstructionCombining/Casts.cs new file mode 100644 index 00000000000000..438b4869b3ccf5 --- /dev/null +++ b/src/tests/JIT/opt/InstructionCombining/Casts.cs @@ -0,0 +1,694 @@ +// 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.CompilerServices; +using Xunit; + +namespace TestMultipleCasts +{ + public class Program + { + [MethodImpl(MethodImplOptions.NoInlining)] + [Fact] + public static int CheckMultipleCasts() + { + bool fail = false; + + // Cast int -> x + if (CastIntSbyte(0x11223344) != 0x44) + { + fail = true; + } + if (CastIntShort(-0x11223344) != -0x3344) + { + fail = true; + } + if (CastIntLong(0x11223344) != 0x11223344) + { + fail = true; + } + + // Cast long -> x + if (CastLongSbyte(0xFFEEDDCCBBAAL) != -0x56) + { + fail = true; + } + if (CastLongShort(0xFFEEDDCCBBAAL) != -0x4456) + { + fail = true; + } + if (CastLongInt(0xFFEEDDCCBBAAL) != -0x22334456) + { + fail = true; + } + + // Cast uint -> x + if (CastUIntByte(0x11223344u) != 0x44) + { + fail = true; + } + if (CastUIntUShort(0x11223344u) != 0x3344) + { + fail = true; + } + if (CastUIntULong(0x11223344u) != 0x11223344ul) + { + fail = true; + } + + // Cast ulong -> x + if (CastULongByte(0xFFEEDDCCBBAAul) != 0xAA) + { + fail = true; + } + if (CastULongUShort(0xFFEEDDCCBBAAul) != 0xBBAA) + { + fail = true; + } + if (CastULongUInt(0xFFEEDDCCBBAAul) != 0xDDCCBBAAu) + { + fail = true; + } + + // Cast int -> x -> int + if (CastIntSbyteInt(0xF0) != -0x10) + { + fail = true; + } + if (CastIntShortInt(0xFF8001) != -0x7FFF) + { + fail = true; + } + if (CastIntLongInt(0x11223344) != 0x11223344) + { + fail = true; + } + + // Cast int -> x -> long + if (CastIntSbyteLong(0x12345678) != 0x78) + { + fail = true; + } + if (CastIntShortLong(0x12345678) != 0x5678) + { + fail = true; + } + + // Cast long -> x -> int + if (CastLongSbyteInt(0xA7L) != -0x59) + { + fail = true; + } + if (CastLongShortInt(0xFFFFFFFF8003L) != -0x7FFD) + { + fail = true; + } + + // Cast long -> x -> long + if (CastLongSbyteLong(0xFEL) != -0x2L) + { + fail = true; + } + if (CastLongShortLong(0xDEADL) != -0x2153L) + { + fail = true; + } + if (CastLongIntLong(0x1ABCDEF00L) != -0x54321100L) + { + fail = true; + } + + // Cast uint -> x -> uint + if (CastUIntByteUInt(0xF0u) != 0xF0u) + { + fail = true; + } + if (CastUIntUShortUInt(0xFF8001u) != 0x8001u) + { + fail = true; + } + + // Cast uint -> x -> ulong + if (CastUIntByteULong(0x12345678u) != 0x78ul) + { + fail = true; + } + if (CastUIntUShortULong(0x12345678u) != 0x5678ul) + { + fail = true; + } + if (CastUIntULongUInt(0x11223344u) != 0x11223344u) + { + fail = true; + } + + // Cast ulong -> x -> uint + if (CastULongByteUInt(0xA7ul) != 0xA7u) + { + fail = true; + } + if (CastULongUShortUInt(0xFFFFFFFF8003ul) != 0x8003u) + { + fail = true; + } + + // Cast ulong -> x -> ulong + if (CastULongByteULong(0xFEul) != 0xFEul) + { + fail = true; + } + if (CastULongUShortULong(0xDEADul) != 0xDEADul) + { + fail = true; + } + if (CastULongUIntULong(0x1ABCDEF00ul) != 0xABCDEF00ul) + { + fail = true; + } + + // Cast int -> long -> x + if (CastIntLongSbyte(0x11223344) != 0x44) + { + fail = true; + } + if (CastIntLongShort(0x11223344) != 0x3344) + { + fail = true; + } + + // Cast uint -> ulong -> x + if (CastUIntULongByte(0x11223344u) != 0x44) + { + fail = true; + } + if (CastUIntULongUShort(0x11223344u) != 0x3344) + { + fail = true; + } + + // Cast long -> int -> short -> sbyte + if (CastLongIntShortSByte(0x11223344) != 0x44) + { + fail = true; + } + + // Cast ulong -> uint -> ushort -> byte + if (CastULongUIntUShortByte(0x11223344u) != 0x44) + { + fail = true; + } + + // Cast sbyte -> short -> int -> long + if (CastSByteShortIntLong(-0x59) != -0x59L) + { + fail = true; + } + + // Cast byte -> ushort -> uint -> ulong + if (CastByteUShortUIntULong(0xA7) != 0xA7ul) + { + fail = true; + } + + // Cast int -> long -> int -> long + if (CastIntSByteIntSByte(-0x15263748) != -0x48) + { + fail = true; + } + if (CastIntShortIntShort(-0x15263748) != -0x3748) + { + fail = true; + } + if (CastIntLongIntLong(-0x15263748) != -0x15263748l) + { + fail = true; + } + + // Cast uint -> x -> uint -> x + if (CastUIntByteUIntByte(0xF0u) != 0xF0) + { + fail = true; + } + if (CastUIntUShortUIntUShort(0xFF8001u) != 0x8001) + { + fail = true; + } + if (CastUIntULongUIntULong(0x11223344u) != 0x11223344ul) + { + fail = true; + } + + // Cast long -> x -> long -> x + if (CastLongSByteLongSByte(0xA7L) != -0x59) + { + fail = true; + } + if (CastLongShortLongShort(0xFFFFFFFF8003L) != -0x7FFD) + { + fail = true; + } + if (CastLongIntLongInt(0x1ABCDEF00L) != -0x54321100) + { + fail = true; + } + + // Cast ulong -> x -> ulong -> x + if (CastULongByteULongByte(0xA7ul) != 0xA7) + { + fail = true; + } + if (CastULongUShortULongUShort(0xFFFFFFFF8003ul) != 0x8003) + { + fail = true; + } + if (CastULongUIntULongUInt(0x1ABCDEF00ul) != 0xABCDEF00u) + { + fail = true; + } + + if (fail) + { + return 101; + } + return 100; + } + + // Cast int -> x + + [MethodImpl(MethodImplOptions.NoInlining)] + static sbyte CastIntSbyte(int x) + { + //ARM64-FULL-LINE: sxtb {{w[0-9]+}}, {{w[0-9]+}} + return (sbyte)x; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static short CastIntShort(int x) + { + //ARM64-FULL-LINE: sxth {{w[0-9]+}}, {{w[0-9]+}} + return (short)x; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static long CastIntLong(int x) + { + //ARM64-FULL-LINE: sxtw {{x[0-9]+}}, {{w[0-9]+}} + return (long)x; + } + + // Cast long -> x + + [MethodImpl(MethodImplOptions.NoInlining)] + static sbyte CastLongSbyte(long x) + { + //ARM64-FULL-LINE: sxtb {{w[0-9]+}}, {{w[0-9]+}} + return (sbyte)x; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static short CastLongShort(long x) + { + //ARM64-FULL-LINE: sxth {{w[0-9]+}}, {{w[0-9]+}} + return (short)x; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int CastLongInt(long x) + { + //ARM64-FULL-LINE: mov {{w[0-9]+}}, {{w[0-9]+}} + return (int)x; + } + + // Cast uint -> x + + [MethodImpl(MethodImplOptions.NoInlining)] + static byte CastUIntByte(uint x) + { + //ARM64-FULL-LINE: uxtb {{w[0-9]+}}, {{w[0-9]+}} + return (byte)x; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static ushort CastUIntUShort(uint x) + { + //ARM64-FULL-LINE: uxth {{w[0-9]+}}, {{w[0-9]+}} + return (ushort)x; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static ulong CastUIntULong(uint x) + { + //ARM64-FULL-LINE: mov {{w[0-9]+}}, {{w[0-9]+}} + return (ulong)x; + } + + // Cast ulong -> x + + [MethodImpl(MethodImplOptions.NoInlining)] + static byte CastULongByte(ulong x) + { + //ARM64-FULL-LINE: uxtb {{w[0-9]+}}, {{w[0-9]+}} + return (byte)x; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static ushort CastULongUShort(ulong x) + { + //ARM64-FULL-LINE: uxth {{w[0-9]+}}, {{w[0-9]+}} + return (ushort)x; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static uint CastULongUInt(ulong x) + { + //ARM64-FULL-LINE: mov {{w[0-9]+}}, {{w[0-9]+}} + return (uint)x; + } + + // Cast int -> x -> int + + [MethodImpl(MethodImplOptions.NoInlining)] + static int CastIntSbyteInt(int x) + { + //ARM64-FULL-LINE: sxtb {{w[0-9]+}}, {{w[0-9]+}} + return (sbyte)x; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int CastIntShortInt(int x) + { + //ARM64-FULL-LINE: sxth {{w[0-9]+}}, {{w[0-9]+}} + return (short)x; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int CastIntLongInt(int x) + { + //ARM64-NOT: sxtw {{x[0-9]+}}, {{w[0-9]+}} + return (int)(long)x; + } + + // Cast int -> x -> long + + [MethodImpl(MethodImplOptions.NoInlining)] + static long CastIntSbyteLong(int x) + { + //ARM64-FULL-LINE: sxtb {{w[0-9]+}}, {{w[0-9]+}} + //ARM64-FULL-LINE: sxtw {{x[0-9]+}}, {{w[0-9]+}} + return (sbyte)x; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static long CastIntShortLong(int x) + { + //ARM64-FULL-LINE: sxth {{w[0-9]+}}, {{w[0-9]+}} + //ARM64-FULL-LINE: sxtw {{x[0-9]+}}, {{w[0-9]+}} + return (short)x; + } + + // Cast long -> x -> int + + [MethodImpl(MethodImplOptions.NoInlining)] + static int CastLongSbyteInt(long x) + { + //ARM64-FULL-LINE: sxtb {{w[0-9]+}}, {{w[0-9]+}} + return (sbyte)x; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int CastLongShortInt(long x) + { + //ARM64-FULL-LINE: sxth {{w[0-9]+}}, {{w[0-9]+}} + return (short)x; + } + + // Cast long -> x -> long + + [MethodImpl(MethodImplOptions.NoInlining)] + static long CastLongSbyteLong(long x) + { + //ARM64-FULL-LINE: sxtb {{w[0-9]+}}, {{w[0-9]+}} + //ARM64-FULL-LINE: sxtw {{x[0-9]+}}, {{w[0-9]+}} + return (sbyte)x; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static long CastLongShortLong(long x) + { + //ARM64-FULL-LINE: sxth {{w[0-9]+}}, {{w[0-9]+}} + //ARM64-FULL-LINE: sxtw {{x[0-9]+}}, {{w[0-9]+}} + return (short)x; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static long CastLongIntLong(long x) + { + //ARM64-FULL-LINE: sxtw {{x[0-9]+}}, {{w[0-9]+}} + return (int)x; + } + + // Cast uint -> x -> uint + + [MethodImpl(MethodImplOptions.NoInlining)] + static uint CastUIntByteUInt(uint x) + { + //ARM64-FULL-LINE: uxtb {{w[0-9]+}}, {{w[0-9]+}} + return (byte)x; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static uint CastUIntUShortUInt(uint x) + { + //ARM64-FULL-LINE: uxth {{w[0-9]+}}, {{w[0-9]+}} + return (ushort)x; + } + + // Cast uint -> x -> ulong + + [MethodImpl(MethodImplOptions.NoInlining)] + static ulong CastUIntByteULong(uint x) + { + //ARM64-FULL-LINE: uxtb {{w[0-9]+}}, {{w[0-9]+}} + //ARM64-FULL-LINE: mov {{w[0-9]+}}, {{w[0-9]+}} + return (byte)x; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static ulong CastUIntUShortULong(uint x) + { + //ARM64-FULL-LINE: uxth {{w[0-9]+}}, {{w[0-9]+}} + //ARM64-FULL-LINE: mov {{w[0-9]+}}, {{w[0-9]+}} + return (ushort)x; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static uint CastUIntULongUInt(uint x) + { + //ARM64-FULL-LINE: mov {{w[0-9]+}}, {{w[0-9]+}} + return (uint)(ulong)x; + } + + // Cast ulong -> x -> uint + + [MethodImpl(MethodImplOptions.NoInlining)] + static uint CastULongByteUInt(ulong x) + { + //ARM64-FULL-LINE: uxtb {{w[0-9]+}}, {{w[0-9]+}} + return (byte)x; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static uint CastULongUShortUInt(ulong x) + { + //ARM64-FULL-LINE: uxth {{w[0-9]+}}, {{w[0-9]+}} + return (ushort)x; + } + + // Cast ulong -> x -> ulong + + [MethodImpl(MethodImplOptions.NoInlining)] + static ulong CastULongByteULong(ulong x) + { + //ARM64-FULL-LINE: uxtb {{w[0-9]+}}, {{w[0-9]+}} + //ARM64-FULL-LINE: mov {{w[0-9]+}}, {{w[0-9]+}} + return (byte)x; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static ulong CastULongUShortULong(ulong x) + { + //ARM64-FULL-LINE: uxth {{w[0-9]+}}, {{w[0-9]+}} + //ARM64-FULL-LINE: mov {{w[0-9]+}}, {{w[0-9]+}} + return (ushort)x; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static ulong CastULongUIntULong(ulong x) + { + //ARM64-FULL-LINE: mov {{w[0-9]+}}, {{w[0-9]+}} + return (uint)x; + } + + // Cast int -> long -> x + + [MethodImpl(MethodImplOptions.NoInlining)] + static sbyte CastIntLongSbyte(int x) + { + //ARM64-FULL-LINE: sxtb {{w[0-9]+}}, {{w[0-9]+}} + return (sbyte)(long)x; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static short CastIntLongShort(int x) + { + //ARM64-FULL-LINE: sxth {{w[0-9]+}}, {{w[0-9]+}} + return (short)(long)x; + } + + // Cast uint -> ulong -> x + + [MethodImpl(MethodImplOptions.NoInlining)] + static byte CastUIntULongByte(uint x) + { + //ARM64-FULL-LINE: uxtb {{w[0-9]+}}, {{w[0-9]+}} + return (byte)(ulong)x; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static ushort CastUIntULongUShort(uint x) + { + //ARM64-FULL-LINE: uxth {{w[0-9]+}}, {{w[0-9]+}} + return (ushort)(ulong)x; + } + + // Cast long -> int -> short -> sbyte + + [MethodImpl(MethodImplOptions.NoInlining)] + static sbyte CastLongIntShortSByte(long x) + { + //ARM64-FULL-LINE: sxtb {{w[0-9]+}}, {{w[0-9]+}} + return (sbyte)(short)(int)x; + } + + // Cast ulong -> uint -> ushort -> byte + + [MethodImpl(MethodImplOptions.NoInlining)] + static byte CastULongUIntUShortByte(ulong x) + { + //ARM64-FULL-LINE: uxtb {{w[0-9]+}}, {{w[0-9]+}} + return (byte)(ushort)(uint)x; + } + + // Cast sbyte -> short -> int -> long + + [MethodImpl(MethodImplOptions.NoInlining)] + static long CastSByteShortIntLong(sbyte x) + { + //ARM64-FULL-LINE: sxtb {{w[0-9]+}}, {{w[0-9]+}} + //ARM64-FULL-LINE: sxtw {{x[0-9]+}}, {{w[0-9]+}} + return (long)(int)(short)x; + } + + // Cast byte -> ushort -> uint -> ulong + + [MethodImpl(MethodImplOptions.NoInlining)] + static ulong CastByteUShortUIntULong(byte x) + { + //ARM64-FULL-LINE: uxtb {{w[0-9]+}}, {{w[0-9]+}} + //ARM64-FULL-LINE: mov {{w[0-9]+}}, {{w[0-9]+}} + return (ulong)(uint)(ushort)x; + } + + // Cast int -> x -> int -> x + + [MethodImpl(MethodImplOptions.NoInlining)] + static sbyte CastIntSByteIntSByte(int x) + { + //ARM64-FULL-LINE: sxtb {{w[0-9]+}}, {{w[0-9]+}} + return (sbyte)(int)(sbyte)x; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static short CastIntShortIntShort(int x) + { + //ARM64-FULL-LINE: sxth {{w[0-9]+}}, {{w[0-9]+}} + return (short)(int)(short)x; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static long CastIntLongIntLong(int x) + { + //ARM64-FULL-LINE: sxtw {{x[0-9]+}}, {{w[0-9]+}} + return (long)(int)(long)x; + } + + // Cast uint -> x -> uint -> x + + [MethodImpl(MethodImplOptions.NoInlining)] + static byte CastUIntByteUIntByte(uint x) + { + //ARM64-FULL-LINE: uxtb {{w[0-9]+}}, {{w[0-9]+}} + return (byte)(uint)(byte)x; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static ushort CastUIntUShortUIntUShort(uint x) + { + //ARM64-FULL-LINE: uxth {{w[0-9]+}}, {{w[0-9]+}} + return (ushort)(uint)(ushort)x; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static ulong CastUIntULongUIntULong(uint x) + { + //ARM64-FULL-LINE: mov {{w[0-9]+}}, {{w[0-9]+}} + return (ulong)(uint)(ulong)x; + } + + // Cast long -> x -> long -> x + + [MethodImpl(MethodImplOptions.NoInlining)] + static sbyte CastLongSByteLongSByte(long x) + { + //ARM64-FULL-LINE: sxtb {{w[0-9]+}}, {{w[0-9]+}} + return (sbyte)(long)(sbyte)x; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static short CastLongShortLongShort(long x) + { + //ARM64-FULL-LINE: sxth {{w[0-9]+}}, {{w[0-9]+}} + return (short)(long)(short)x; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int CastLongIntLongInt(long x) + { + //ARM64-FULL-LINE: mov {{w[0-9]+}}, {{w[0-9]+}} + return (int)(long)(int)x; + } + + // Cast ulong -> x -> ulong -> x + + [MethodImpl(MethodImplOptions.NoInlining)] + static byte CastULongByteULongByte(ulong x) + { + //ARM64-FULL-LINE: uxtb {{w[0-9]+}}, {{w[0-9]+}} + return (byte)(ulong)(byte)x; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static ushort CastULongUShortULongUShort(ulong x) + { + //ARM64-FULL-LINE: uxth {{w[0-9]+}}, {{w[0-9]+}} + return (ushort)(ulong)(ushort)x; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static uint CastULongUIntULongUInt(ulong x) + { + //ARM64-FULL-LINE: mov {{w[0-9]+}}, {{w[0-9]+}} + return (uint)(ulong)(uint)x; + } + } +} diff --git a/src/tests/JIT/opt/InstructionCombining/Casts.csproj b/src/tests/JIT/opt/InstructionCombining/Casts.csproj new file mode 100644 index 00000000000000..b3cc1eeece28c6 --- /dev/null +++ b/src/tests/JIT/opt/InstructionCombining/Casts.csproj @@ -0,0 +1,17 @@ + + + + true + + + None + True + + + + true + + + + + diff --git a/src/tests/async/devirtualize/devirtualize.cs b/src/tests/async/devirtualize/devirtualize.cs new file mode 100644 index 00000000000000..5e7af86a5ee3ee --- /dev/null +++ b/src/tests/async/devirtualize/devirtualize.cs @@ -0,0 +1,144 @@ +// 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.Runtime.CompilerServices; +using System.Threading.Tasks; +using Xunit; + +public class Async2Devirtualize +{ + class Base + { + public virtual async Task GetValue() + { + await Task.Yield(); + return 1; + } + } + + // Non-sealed derived class: devirtualization must go through resolveVirtualMethod + // because the JIT cannot use the sealed-type shortcut. + class OpenDerived : Base + { + public override async Task GetValue() + { + await Task.Yield(); + return 42; + } + } + + sealed class SealedDerived : Base + { + public override async Task GetValue() + { + await Task.Yield(); + return 2; + } + } + + sealed class SealedDerivedNoYield : Base + { + [RuntimeAsyncMethodGeneration(false)] + public override async Task GetValue() + { + await Task.Yield(); + return 3; + } + } + + interface IAsyncInterface + { + Task GetValue(); + } + + // Non-sealed interface implementation: devirtualization of the interface + // call must go through resolveVirtualMethod. + class OpenInterfaceImpl : IAsyncInterface + { + public virtual async Task GetValue() + { + await Task.Yield(); + return 43; + } + } + + sealed class SealedInterfaceImpl : IAsyncInterface + { + public async Task GetValue() + { + await Task.Yield(); + return 10; + } + } + + sealed class SealedInterfaceImplNoYield : IAsyncInterface + { + [RuntimeAsyncMethodGeneration(false)] + public async Task GetValue() + { + await Task.Yield(); + return 11; + } + } + + // Uses newobj to give the JIT exact type info on a non-sealed type. + // The JIT must call resolveVirtualMethod to devirtualize. + [MethodImpl(MethodImplOptions.NoInlining)] + static async Task CallOnNewOpenDerived() + { + Base obj = new OpenDerived(); + return await obj.GetValue(); + } + + // Non-sealed interface impl with exact type from newobj. + [MethodImpl(MethodImplOptions.NoInlining)] + static async Task CallOnNewOpenInterfaceImpl() + { + IAsyncInterface obj = new OpenInterfaceImpl(); + return await obj.GetValue(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static async Task CallOnSealed(SealedDerived obj) + { + return await obj.GetValue(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static async Task CallOnSealedNoYield(SealedDerivedNoYield obj) + { + return await obj.GetValue(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static async Task CallOnSealedInterface(SealedInterfaceImpl obj) + { + return await obj.GetValue(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static async Task CallOnSealedInterfaceNoYield(SealedInterfaceImplNoYield obj) + { + return await obj.GetValue(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static async Task CallOnInterface(T obj) where T : IAsyncInterface + { + return await obj.GetValue(); + } + + [Fact] + public static void TestEntryPoint() + { + Assert.Equal(42, CallOnNewOpenDerived().Result); + Assert.Equal(43, CallOnNewOpenInterfaceImpl().Result); + Assert.Equal(2, CallOnSealed(new SealedDerived()).Result); + Assert.Equal(3, CallOnSealedNoYield(new SealedDerivedNoYield()).Result); + Assert.Equal(10, CallOnSealedInterface(new SealedInterfaceImpl()).Result); + Assert.Equal(11, CallOnSealedInterfaceNoYield(new SealedInterfaceImplNoYield()).Result); + Assert.Equal(10, CallOnInterface(new SealedInterfaceImpl()).Result); + Assert.Equal(11, CallOnInterface(new SealedInterfaceImplNoYield()).Result); + } +} diff --git a/src/tests/async/devirtualize/devirtualize.csproj b/src/tests/async/devirtualize/devirtualize.csproj new file mode 100644 index 00000000000000..197767e2c4e249 --- /dev/null +++ b/src/tests/async/devirtualize/devirtualize.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/tests/nativeaot/Directory.Build.props b/src/tests/nativeaot/Directory.Build.props index 1e08092b2bc7f1..17342717f61c8e 100644 --- a/src/tests/nativeaot/Directory.Build.props +++ b/src/tests/nativeaot/Directory.Build.props @@ -5,11 +5,5 @@ true - - - true diff --git a/src/tests/nativeaot/Directory.Build.targets b/src/tests/nativeaot/Directory.Build.targets deleted file mode 100644 index ba8535b823ebf5..00000000000000 --- a/src/tests/nativeaot/Directory.Build.targets +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - diff --git a/src/tests/nativeaot/MobileSmokeTest/MobileSmokeTest.cs b/src/tests/nativeaot/MobileSmokeTest/MobileSmokeTest.cs new file mode 100644 index 00000000000000..2297d5641251ec --- /dev/null +++ b/src/tests/nativeaot/MobileSmokeTest/MobileSmokeTest.cs @@ -0,0 +1,28 @@ +// 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.Runtime.CompilerServices; + +unsafe class Program +{ + [MethodImpl(MethodImplOptions.NoInlining)] + static int ReadPointer(int* pVal) + { + return *pVal; + } + + static int Main() + { + try + { + ReadPointer(null); + } + catch (Exception) + { + return 100; + } + + return 1; + } +} diff --git a/src/tests/nativeaot/MobileSmokeTest/MobileSmokeTest.csproj b/src/tests/nativeaot/MobileSmokeTest/MobileSmokeTest.csproj new file mode 100644 index 00000000000000..fe1da589c74ba1 --- /dev/null +++ b/src/tests/nativeaot/MobileSmokeTest/MobileSmokeTest.csproj @@ -0,0 +1,14 @@ + + + Exe + false + true + true + + + + + + + + diff --git a/src/tests/nativeaot/StartupHook/StartupHook.csproj b/src/tests/nativeaot/StartupHook/StartupHook.csproj index 046dce277364a3..ed6e89a9fc27db 100644 --- a/src/tests/nativeaot/StartupHook/StartupHook.csproj +++ b/src/tests/nativeaot/StartupHook/StartupHook.csproj @@ -5,8 +5,8 @@ true true $(NoWarn);IL2026 + true false - true diff --git a/src/tests/nativeaot/nativeaot.csproj b/src/tests/nativeaot/nativeaot.csproj new file mode 100644 index 00000000000000..21db07dd33d756 --- /dev/null +++ b/src/tests/nativeaot/nativeaot.csproj @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/TestFrameworkTests.g.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/TestFrameworkTests.g.cs index 9a3a6d49cd8492..3d62106f5cbe2e 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/TestFrameworkTests.g.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/TestFrameworkTests.g.cs @@ -93,6 +93,18 @@ public Task VerifyDefineAttributeBehavior() return RunTest(allowMissingWarnings: true); } + [Fact] + public Task VerifyKeptAttributeAttributeWorks() + { + return RunTest(allowMissingWarnings: true); + } + + [Fact] + public Task VerifyLocalsAreChanged() + { + return RunTest(allowMissingWarnings: true); + } + [Fact] public Task VerifyResourceInAssemblyAttributesBehavior() { diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptAttributeAttribute.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptAttributeAttribute.cs index b07a1c0a96a928..c8f50eb4af4962 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptAttributeAttribute.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptAttributeAttribute.cs @@ -19,5 +19,18 @@ public KeptAttributeAttribute(Type type) { ArgumentNullException.ThrowIfNull(type); } + + /// + /// Use this constructor when you want to explicitly verify that an attribute with specific parameters survived. + /// + /// This is useful when you have a test that has multiple attributes of the same type but passed each is passed different parameters + /// and you want to verify which one(s) survived + /// + /// + /// + public KeptAttributeAttribute(Type type, params object[] args) + { + ArgumentNullException.ThrowIfNull(type); + } } } diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeMap.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeMap.cs index 558306f20960eb..56867194edec4c 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeMap.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeMap.cs @@ -14,11 +14,6 @@ using Mono.Linker.Tests.Cases.Reflection.Dependencies; using Mono.Linker.Tests.Cases.Reflection.Dependencies.Library; -[assembly: KeptAttributeAttribute(typeof(TypeMapAttribute))] -[assembly: KeptAttributeAttribute(typeof(TypeMapAssociationAttribute))] -[assembly: KeptAttributeAttribute(typeof(TypeMapAssociationAttribute))] -[assembly: KeptAttributeAttribute(typeof(TypeMapAttribute))] -[assembly: KeptAttributeAttribute(typeof(TypeMapAttribute))] [assembly: TypeMap("TrimTargetIsTarget", typeof(TargetAndTrimTarget), typeof(TargetAndTrimTarget))] [assembly: TypeMap("TrimTargetIsUnrelated", typeof(TargetType), typeof(TrimTarget))] [assembly: TypeMap(nameof(AllocatedNoTypeCheckClassTarget), typeof(AllocatedNoTypeCheckClassTarget), typeof(AllocatedNoTypeCheckClass))] @@ -33,14 +28,29 @@ [assembly: TypeMap("Ldobj", typeof(LdobjTarget), typeof(LdobjType))] [assembly: TypeMap("ArrayElement", typeof(ArrayElementTarget), typeof(ArrayElement))] [assembly: TypeMap("TrimTargetIsAllocatedNoTypeCheckNoBoxStruct", typeof(ConstructedNoTypeCheckOrBoxTarget), typeof(ConstructedNoTypeCheckNoBoxStruct))] +[assembly: KeptAttributeAttribute(typeof(TypeMapAttribute), "TrimTargetIsTarget", typeof(TargetAndTrimTarget), typeof(TargetAndTrimTarget))] +[assembly: KeptAttributeAttribute(typeof(TypeMapAttribute), "TrimTargetIsUnrelated", typeof(TargetType), typeof(TrimTarget))] +[assembly: KeptAttributeAttribute(typeof(TypeMapAttribute), nameof(AllocatedNoTypeCheckClassTarget), typeof(AllocatedNoTypeCheckClassTarget), typeof(AllocatedNoTypeCheckClass))] +[assembly: KeptAttributeAttribute(typeof(TypeMapAttribute), nameof(AlloctedNoTypeCheckStructTarget), typeof(AlloctedNoTypeCheckStructTarget), typeof(AllocatedNoTypeCheckStruct))] +[assembly: KeptAttributeAttribute(typeof(TypeMapAttribute), "TypeMapEntryOnly", typeof(TypeMapEntryOnly))] +[assembly: KeptAttributeAttribute(typeof(TypeMapAttribute), nameof(UnboxedOnlyTarget), typeof(UnboxedOnlyTarget), typeof(UnboxedOnly))] +[assembly: KeptAttributeAttribute(typeof(TypeMapAttribute), "TypedRefSource", typeof(MakeRefTargetType), typeof(MakeRef))] +[assembly: KeptAttributeAttribute(typeof(TypeMapAttribute), "TypedRefTarget", typeof(RefValueTargetType), typeof(RefValue))] +[assembly: KeptAttributeAttribute(typeof(TypeMapAttribute), "Constrained", typeof(ConstrainedTarget), typeof(Constrained))] +[assembly: KeptAttributeAttribute(typeof(TypeMapAttribute), "ConstrainedStatic", typeof(ConstraintedStaticTarget), typeof(ConstrainedStatic))] +[assembly: KeptAttributeAttribute(typeof(TypeMapAttribute), "Ldobj", typeof(LdobjTarget), typeof(LdobjType))] +[assembly: KeptAttributeAttribute(typeof(TypeMapAttribute), "ArrayElement", typeof(ArrayElementTarget), typeof(ArrayElement))] +[assembly: KeptAttributeAttribute(typeof(TypeMapAttribute), "TrimTargetIsAllocatedNoTypeCheckNoBoxStruct", typeof(ConstructedNoTypeCheckOrBoxTarget), typeof(ConstructedNoTypeCheckNoBoxStruct))] // The TypeMap Universes are kept separate such that Proxy attributes shouldn't be kept if only the External type map is needed. [assembly: TypeMap("UsedOnlyForExternalTypeMap", typeof(UsedExternalTarget), typeof(UsedTrimTarget))] // Kept +[assembly: KeptAttributeAttribute(typeof(TypeMapAttribute), "UsedOnlyForExternalTypeMap", typeof(UsedExternalTarget), typeof(UsedTrimTarget))] [assembly: TypeMapAssociation(typeof(UsedProxySource), typeof(UsedProxyTarget))] // Removed // The TypeMap Universes are kept separate such that External attributes shouldn't be kept if only the Proxy type map is needed. [assembly: TypeMap("UsedOnlyForExternalTypeMap", typeof(UsedExternalTarget2), typeof(UsedTrimTarget2))] // Removed [assembly: TypeMapAssociation(typeof(UsedProxySource2), typeof(UsedProxyTarget2))] // Kept +[assembly: KeptAttributeAttribute(typeof(TypeMapAssociationAttribute), typeof(UsedProxySource2), typeof(UsedProxyTarget2))] [assembly: TypeMapAssociation(typeof(SourceClass), typeof(ProxyType))] [assembly: TypeMapAssociation(typeof(TypeCheckOnlyClass), typeof(TypeCheckOnlyProxy))] @@ -48,6 +58,10 @@ [assembly: TypeMapAssociation(typeof(I), typeof(IImpl))] [assembly: TypeMapAssociation(typeof(IInterfaceWithDynamicImpl), typeof(IDynamicImpl))] [assembly: TypeMapAssociation(typeof(ArrayElement), typeof(ArrayElementProxy))] +[assembly: KeptAttributeAttribute(typeof(TypeMapAssociationAttribute), typeof(SourceClass), typeof(ProxyType))] +[assembly: KeptAttributeAttribute(typeof(TypeMapAssociationAttribute), typeof(AllocatedNoBoxStructType), typeof(AllocatedNoBoxProxy))] +[assembly: KeptAttributeAttribute(typeof(TypeMapAssociationAttribute), typeof(IInterfaceWithDynamicImpl), typeof(IDynamicImpl))] +[assembly: KeptAttributeAttribute(typeof(TypeMapAssociationAttribute), typeof(ArrayElement), typeof(ArrayElementProxy))] [assembly: TypeMap("UnusedName", typeof(UnusedTargetType), typeof(TrimTarget))] [assembly: TypeMapAssociation(typeof(UnusedSourceClass), typeof(UnusedProxyType))] @@ -55,6 +69,7 @@ [assembly: TypeMap("ClassWithStaticMethodAndField", typeof(TargetType5), typeof(ClassWithStaticMethodAndField))] [assembly: TypeMap("UnimportantString", typeof(PreservedTargetType))] +[assembly: KeptAttributeAttribute(typeof(TypeMapAttribute), "UnimportantString", typeof(PreservedTargetType))] [assembly: KeptAttributeAttribute(typeof(TypeMapAssemblyTargetAttribute))] [assembly: TypeMapAssemblyTarget("library")] diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/TestFramework/VerifyKeptAttributeAttributeWorks.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/TestFramework/VerifyKeptAttributeAttributeWorks.cs new file mode 100644 index 00000000000000..d8e33a8ec62acc --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/TestFramework/VerifyKeptAttributeAttributeWorks.cs @@ -0,0 +1,310 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.TestFramework; + +[assembly: VerifyKeptAttributeAttributeWorks.NoArguments] +[assembly: KeptAttributeAttribute(typeof(VerifyKeptAttributeAttributeWorks.NoArgumentsAttribute))] + +[assembly: VerifyKeptAttributeAttributeWorks.NoArgumentsWithDuplicates] +[assembly: VerifyKeptAttributeAttributeWorks.NoArgumentsWithDuplicates] +// Roslyn will dedupe so only need one assert +[assembly: KeptAttributeAttribute(typeof(VerifyKeptAttributeAttributeWorks.NoArgumentsWithDuplicatesAttribute))] + +[assembly: VerifyKeptAttributeAttributeWorks.WithArgumentsLooseAssert("arg1", 1, "arg3", typeof(int))] +[assembly: KeptAttributeAttribute(typeof(VerifyKeptAttributeAttributeWorks.WithArgumentsLooseAssertAttribute))] + +[assembly: VerifyKeptAttributeAttributeWorks.WithArgumentsExplicitAssert("arg1", 1, "arg3", typeof(int))] +[assembly: KeptAttributeAttribute(typeof(VerifyKeptAttributeAttributeWorks.WithArgumentsExplicitAssertAttribute), "arg1", 1, "arg3", typeof(int))] + +[assembly: VerifyKeptAttributeAttributeWorks.WithArgumentsWithDuplicatesExplicitAssert("inst1-arg1", 1, "inst1-arg3", typeof(int))] +[assembly: VerifyKeptAttributeAttributeWorks.WithArgumentsWithDuplicatesExplicitAssert("inst2-arg1", 2, "inst2-arg3", typeof(string))] +[assembly: VerifyKeptAttributeAttributeWorks.WithArgumentsWithDuplicatesExplicitAssert("inst3-arg1", 3, "inst3-arg3", typeof(VerifyKeptAttributeAttributeWorks.Foo))] +// Intentionally make kept attribute ordering different than the usages to verify we don't require ordering of kept attributes to match the usages +[assembly: KeptAttributeAttribute(typeof(VerifyKeptAttributeAttributeWorks.WithArgumentsWithDuplicatesExplicitAssertAttribute), "inst1-arg1", 1, "inst1-arg3", typeof(int))] +[assembly: KeptAttributeAttribute(typeof(VerifyKeptAttributeAttributeWorks.WithArgumentsWithDuplicatesExplicitAssertAttribute), "inst3-arg1", 3, "inst3-arg3", typeof(VerifyKeptAttributeAttributeWorks.Foo))] +[assembly: KeptAttributeAttribute(typeof(VerifyKeptAttributeAttributeWorks.WithArgumentsWithDuplicatesExplicitAssertAttribute), "inst2-arg1", 2, "inst2-arg3", typeof(string))] + +[assembly: VerifyKeptAttributeAttributeWorks.WithArgumentsWithGenericWithDuplicatesExplicitAssert("inst1-arg1", 1, "inst1-arg3", typeof(int))] +[assembly: VerifyKeptAttributeAttributeWorks.WithArgumentsWithGenericWithDuplicatesExplicitAssert("inst2-arg1", 2, "inst2-arg3", typeof(string))] +[assembly: VerifyKeptAttributeAttributeWorks.WithArgumentsWithGenericWithDuplicatesExplicitAssert("inst3-arg1", 3, "inst3-arg3", typeof(VerifyKeptAttributeAttributeWorks.Foo))] +// Intentionally make kept attribute ordering different than the usages to verify we don't require ordering of kept attributes to match the usages +[assembly: KeptAttributeAttribute(typeof(VerifyKeptAttributeAttributeWorks.WithArgumentsWithGenericWithDuplicatesExplicitAssertAttribute), "inst1-arg1", 1, "inst1-arg3", typeof(int))] +[assembly: KeptAttributeAttribute(typeof(VerifyKeptAttributeAttributeWorks.WithArgumentsWithGenericWithDuplicatesExplicitAssertAttribute), "inst3-arg1", 3, "inst3-arg3", typeof(VerifyKeptAttributeAttributeWorks.Foo))] +[assembly: KeptAttributeAttribute(typeof(VerifyKeptAttributeAttributeWorks.WithArgumentsWithGenericWithDuplicatesExplicitAssertAttribute), "inst2-arg1", 2, "inst2-arg3", typeof(string))] + +namespace Mono.Linker.Tests.Cases.TestFramework; + +public class VerifyKeptAttributeAttributeWorks +{ + public static void Main() + { + var f = new Foo(); + f.Method(); + f.Field = 1; + f.Property = 1; + f.Event += delegate(object sender, EventArgs e) + { + }; + } + + [Kept] + [KeptMember(".ctor()")] + + [NoArguments] + [KeptAttributeAttribute(typeof(NoArgumentsAttribute))] + + [NoArgumentsWithDuplicates] + [NoArgumentsWithDuplicates] + [KeptAttributeAttribute(typeof(NoArgumentsWithDuplicatesAttribute))] + [KeptAttributeAttribute(typeof(NoArgumentsWithDuplicatesAttribute))] + + [WithArgumentsLooseAssert("arg1", 1, "arg3", typeof(int))] + [KeptAttributeAttribute(typeof(WithArgumentsLooseAssertAttribute))] + + [WithArgumentsExplicitAssert("arg1", 1, "arg3", typeof(int))] + [KeptAttributeAttribute(typeof(WithArgumentsExplicitAssertAttribute), "arg1", 1, "arg3", typeof(int))] + + [WithArgumentsWithDuplicatesLooseAssert("arg1", 1, "arg3", typeof(int))] + [WithArgumentsWithDuplicatesLooseAssert("arg1", 1, "arg3", typeof(int))] + [WithArgumentsWithDuplicatesLooseAssert("arg1", 2, "arg3", typeof(int))] + [KeptAttributeAttribute(typeof(WithArgumentsWithDuplicatesLooseAssertAttribute))] + [KeptAttributeAttribute(typeof(WithArgumentsWithDuplicatesLooseAssertAttribute))] + [KeptAttributeAttribute(typeof(WithArgumentsWithDuplicatesLooseAssertAttribute))] + + [WithArgumentsWithDuplicatesExplicitAssert("inst1-arg1", 1, "inst1-arg3", typeof(int))] + [WithArgumentsWithDuplicatesExplicitAssert("inst2-arg1", 2, "inst2-arg3", typeof(string))] + [WithArgumentsWithDuplicatesExplicitAssert("inst3-arg1", 3, "inst3-arg3", typeof(Foo))] + [WithArgumentsWithDuplicatesExplicitAssert(null, 4, null, null)] + // Intentionally make kept attribute ordering different than the usages to verify we don't require ordering of kept attributes to match the usages + [KeptAttributeAttribute(typeof(WithArgumentsWithDuplicatesExplicitAssertAttribute), "inst1-arg1", 1, "inst1-arg3", typeof(int))] + [KeptAttributeAttribute(typeof(WithArgumentsWithDuplicatesExplicitAssertAttribute), "inst3-arg1", 3, "inst3-arg3", typeof(Foo))] + [KeptAttributeAttribute(typeof(WithArgumentsWithDuplicatesExplicitAssertAttribute), "inst2-arg1", 2, "inst2-arg3", typeof(string))] + [KeptAttributeAttribute(typeof(WithArgumentsWithDuplicatesExplicitAssertAttribute), null, 4, null, null)] + + [WithArgumentsWithGenericWithDuplicatesExplicitAssert("inst1-arg1", 1, "inst1-arg3", typeof(int))] + [WithArgumentsWithGenericWithDuplicatesExplicitAssert("inst2-arg1", 2, "inst2-arg3", typeof(string))] + [WithArgumentsWithGenericWithDuplicatesExplicitAssert("inst3-arg1", 3, "inst3-arg3", typeof(Foo))] + // Intentionally make kept attribute ordering different than the usages to verify we don't require ordering of kept attributes to match the usages + [KeptAttributeAttribute(typeof(WithArgumentsWithGenericWithDuplicatesExplicitAssertAttribute), "inst1-arg1", 1, "inst1-arg3", typeof(int))] + [KeptAttributeAttribute(typeof(WithArgumentsWithGenericWithDuplicatesExplicitAssertAttribute), "inst3-arg1", 3, "inst3-arg3", typeof(Foo))] + [KeptAttributeAttribute(typeof(WithArgumentsWithGenericWithDuplicatesExplicitAssertAttribute), "inst2-arg1", 2, "inst2-arg3", typeof(string))] + public class Foo + { + [Kept] + + [NoArguments] + [KeptAttributeAttribute(typeof(NoArgumentsAttribute))] + + [NoArgumentsWithDuplicates] + [NoArgumentsWithDuplicates] + [KeptAttributeAttribute(typeof(NoArgumentsWithDuplicatesAttribute))] + [KeptAttributeAttribute(typeof(NoArgumentsWithDuplicatesAttribute))] + + [WithArgumentsLooseAssert("arg1", 1, "arg3", typeof(int))] + [KeptAttributeAttribute(typeof(WithArgumentsLooseAssertAttribute))] + + [WithArgumentsExplicitAssert("arg1", 1, "arg3", typeof(int))] + [KeptAttributeAttribute(typeof(WithArgumentsExplicitAssertAttribute), "arg1", 1, "arg3", typeof(int))] + + [WithArgumentsWithDuplicatesLooseAssert("arg1", 1, "arg3", typeof(int))] + [WithArgumentsWithDuplicatesLooseAssert("arg1", 1, "arg3", typeof(int))] + [WithArgumentsWithDuplicatesLooseAssert("arg1", 2, "arg3", typeof(int))] + [KeptAttributeAttribute(typeof(WithArgumentsWithDuplicatesLooseAssertAttribute))] + [KeptAttributeAttribute(typeof(WithArgumentsWithDuplicatesLooseAssertAttribute))] + [KeptAttributeAttribute(typeof(WithArgumentsWithDuplicatesLooseAssertAttribute))] + + [WithArgumentsWithDuplicatesExplicitAssert("inst1-arg1", 1, "inst1-arg3", typeof(int))] + [WithArgumentsWithDuplicatesExplicitAssert("inst2-arg1", 2, "inst2-arg3", typeof(string))] + [WithArgumentsWithDuplicatesExplicitAssert("inst3-arg1", 3, "inst3-arg3", typeof(Foo))] + // Intentionally make kept attribute ordering different than the usages to verify we don't require ordering of kept attributes to match the usages + [KeptAttributeAttribute(typeof(WithArgumentsWithDuplicatesExplicitAssertAttribute), "inst1-arg1", 1, "inst1-arg3", typeof(int))] + [KeptAttributeAttribute(typeof(WithArgumentsWithDuplicatesExplicitAssertAttribute), "inst3-arg1", 3, "inst3-arg3", typeof(Foo))] + [KeptAttributeAttribute(typeof(WithArgumentsWithDuplicatesExplicitAssertAttribute), "inst2-arg1", 2, "inst2-arg3", typeof(string))] + public int Field; + + [Kept] + + [NoArguments] + [KeptAttributeAttribute(typeof(NoArgumentsAttribute))] + + [NoArgumentsWithDuplicates] + [NoArgumentsWithDuplicates] + [KeptAttributeAttribute(typeof(NoArgumentsWithDuplicatesAttribute))] + [KeptAttributeAttribute(typeof(NoArgumentsWithDuplicatesAttribute))] + + [WithArgumentsLooseAssert("arg1", 1, "arg3", typeof(int))] + [KeptAttributeAttribute(typeof(WithArgumentsLooseAssertAttribute))] + + [WithArgumentsExplicitAssert("arg1", 1, "arg3", typeof(int))] + [KeptAttributeAttribute(typeof(WithArgumentsExplicitAssertAttribute), "arg1", 1, "arg3", typeof(int))] + + [WithArgumentsWithDuplicatesLooseAssert("arg1", 1, "arg3", typeof(int))] + [WithArgumentsWithDuplicatesLooseAssert("arg1", 1, "arg3", typeof(int))] + [WithArgumentsWithDuplicatesLooseAssert("arg1", 2, "arg3", typeof(int))] + [KeptAttributeAttribute(typeof(WithArgumentsWithDuplicatesLooseAssertAttribute))] + [KeptAttributeAttribute(typeof(WithArgumentsWithDuplicatesLooseAssertAttribute))] + [KeptAttributeAttribute(typeof(WithArgumentsWithDuplicatesLooseAssertAttribute))] + + [WithArgumentsWithDuplicatesExplicitAssert("inst1-arg1", 1, "inst1-arg3", typeof(int))] + [WithArgumentsWithDuplicatesExplicitAssert("inst2-arg1", 2, "inst2-arg3", typeof(string))] + [WithArgumentsWithDuplicatesExplicitAssert("inst3-arg1", 3, "inst3-arg3", typeof(Foo))] + // Intentionally make kept attribute ordering different than the usages to verify we don't require ordering of kept attributes to match the usages + [KeptAttributeAttribute(typeof(WithArgumentsWithDuplicatesExplicitAssertAttribute), "inst1-arg1", 1, "inst1-arg3", typeof(int))] + [KeptAttributeAttribute(typeof(WithArgumentsWithDuplicatesExplicitAssertAttribute), "inst3-arg1", 3, "inst3-arg3", typeof(Foo))] + [KeptAttributeAttribute(typeof(WithArgumentsWithDuplicatesExplicitAssertAttribute), "inst2-arg1", 2, "inst2-arg3", typeof(string))] + public void Method() + { + } + + [Kept] + + [NoArguments] + [KeptAttributeAttribute(typeof(NoArgumentsAttribute))] + + [NoArgumentsWithDuplicates] + [NoArgumentsWithDuplicates] + [KeptAttributeAttribute(typeof(NoArgumentsWithDuplicatesAttribute))] + [KeptAttributeAttribute(typeof(NoArgumentsWithDuplicatesAttribute))] + + [WithArgumentsLooseAssert("arg1", 1, "arg3", typeof(int))] + [KeptAttributeAttribute(typeof(WithArgumentsLooseAssertAttribute))] + + [WithArgumentsExplicitAssert("arg1", 1, "arg3", typeof(int))] + [KeptAttributeAttribute(typeof(WithArgumentsExplicitAssertAttribute), "arg1", 1, "arg3", typeof(int))] + + [WithArgumentsWithDuplicatesLooseAssert("arg1", 1, "arg3", typeof(int))] + [WithArgumentsWithDuplicatesLooseAssert("arg1", 1, "arg3", typeof(int))] + [WithArgumentsWithDuplicatesLooseAssert("arg1", 2, "arg3", typeof(int))] + [KeptAttributeAttribute(typeof(WithArgumentsWithDuplicatesLooseAssertAttribute))] + [KeptAttributeAttribute(typeof(WithArgumentsWithDuplicatesLooseAssertAttribute))] + [KeptAttributeAttribute(typeof(WithArgumentsWithDuplicatesLooseAssertAttribute))] + + [WithArgumentsWithDuplicatesExplicitAssert("inst1-arg1", 1, "inst1-arg3", typeof(int))] + [WithArgumentsWithDuplicatesExplicitAssert("inst2-arg1", 2, "inst2-arg3", typeof(string))] + [WithArgumentsWithDuplicatesExplicitAssert("inst3-arg1", 3, "inst3-arg3", typeof(Foo))] + // Intentionally make kept attribute ordering different than the usages to verify we don't require ordering of kept attributes to match the usages + [KeptAttributeAttribute(typeof(WithArgumentsWithDuplicatesExplicitAssertAttribute), "inst1-arg1", 1, "inst1-arg3", typeof(int))] + [KeptAttributeAttribute(typeof(WithArgumentsWithDuplicatesExplicitAssertAttribute), "inst3-arg1", 3, "inst3-arg3", typeof(Foo))] + [KeptAttributeAttribute(typeof(WithArgumentsWithDuplicatesExplicitAssertAttribute), "inst2-arg1", 2, "inst2-arg3", typeof(string))] + public int Property; + + [Kept] + [KeptBackingField] + [KeptEventAddMethod] + [KeptEventRemoveMethod] + + [NoArguments] + [KeptAttributeAttribute(typeof(NoArgumentsAttribute))] + + [NoArgumentsWithDuplicates] + [NoArgumentsWithDuplicates] + [KeptAttributeAttribute(typeof(NoArgumentsWithDuplicatesAttribute))] + [KeptAttributeAttribute(typeof(NoArgumentsWithDuplicatesAttribute))] + + [WithArgumentsLooseAssert("arg1", 1, "arg3", typeof(int))] + [KeptAttributeAttribute(typeof(WithArgumentsLooseAssertAttribute))] + + [WithArgumentsExplicitAssert("arg1", 1, "arg3", typeof(int))] + [KeptAttributeAttribute(typeof(WithArgumentsExplicitAssertAttribute), "arg1", 1, "arg3", typeof(int))] + + [WithArgumentsWithDuplicatesLooseAssert("arg1", 1, "arg3", typeof(int))] + [WithArgumentsWithDuplicatesLooseAssert("arg1", 1, "arg3", typeof(int))] + [WithArgumentsWithDuplicatesLooseAssert("arg1", 2, "arg3", typeof(int))] + [KeptAttributeAttribute(typeof(WithArgumentsWithDuplicatesLooseAssertAttribute))] + [KeptAttributeAttribute(typeof(WithArgumentsWithDuplicatesLooseAssertAttribute))] + [KeptAttributeAttribute(typeof(WithArgumentsWithDuplicatesLooseAssertAttribute))] + + [WithArgumentsWithDuplicatesExplicitAssert("inst1-arg1", 1, "inst1-arg3", typeof(int))] + [WithArgumentsWithDuplicatesExplicitAssert("inst2-arg1", 2, "inst2-arg3", typeof(string))] + [WithArgumentsWithDuplicatesExplicitAssert("inst3-arg1", 3, "inst3-arg3", typeof(Foo))] + // Intentionally make kept attribute ordering different than the usages to verify we don't require ordering of kept attributes to match the usages + [KeptAttributeAttribute(typeof(WithArgumentsWithDuplicatesExplicitAssertAttribute), "inst1-arg1", 1, "inst1-arg3", typeof(int))] + [KeptAttributeAttribute(typeof(WithArgumentsWithDuplicatesExplicitAssertAttribute), "inst3-arg1", 3, "inst3-arg3", typeof(Foo))] + [KeptAttributeAttribute(typeof(WithArgumentsWithDuplicatesExplicitAssertAttribute), "inst2-arg1", 2, "inst2-arg3", typeof(string))] + public event EventHandler Event; + } + + [Kept] + [KeptMember(".ctor()")] + [KeptBaseType(typeof(Attribute))] + [KeptAttributeAttribute(typeof(AttributeUsageAttribute))] + [AttributeUsage(AttributeTargets.All)] + public class NoArgumentsAttribute : Attribute + { + } + + [Kept] + [KeptBaseType(typeof(Attribute))] + [KeptAttributeAttribute(typeof(AttributeUsageAttribute))] + [AttributeUsage(AttributeTargets.All)] + public class WithArgumentsLooseAssertAttribute : Attribute + { + [Kept] + public WithArgumentsLooseAssertAttribute(object arg1, int arg2, string arg3, Type arg4) + { + } + } + + [Kept] + [KeptBaseType(typeof(Attribute))] + [KeptAttributeAttribute(typeof(AttributeUsageAttribute))] + [AttributeUsage(AttributeTargets.All)] + public class WithArgumentsExplicitAssertAttribute : Attribute + { + [Kept] + public WithArgumentsExplicitAssertAttribute(object arg1, int arg2, string arg3, Type arg4) + { + } + } + + [Kept] + [KeptMember(".ctor()")] + [KeptBaseType(typeof(Attribute))] + [KeptAttributeAttribute(typeof(AttributeUsageAttribute))] + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class NoArgumentsWithDuplicatesAttribute : Attribute + { + } + + [Kept] + [KeptMember(".ctor()")] + [KeptBaseType(typeof(Attribute))] + [KeptAttributeAttribute(typeof(AttributeUsageAttribute))] + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class WithArgumentsWithDuplicatesLooseAssertAttribute : Attribute + { + [Kept] + public WithArgumentsWithDuplicatesLooseAssertAttribute(object arg1, int arg2, string arg3, Type arg4) + { + } + } + + [Kept] + [KeptMember(".ctor()")] + [KeptBaseType(typeof(Attribute))] + [KeptAttributeAttribute(typeof(AttributeUsageAttribute))] + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class WithArgumentsWithDuplicatesExplicitAssertAttribute : Attribute + { + [Kept] + public WithArgumentsWithDuplicatesExplicitAssertAttribute(object arg1, int arg2, string arg3, Type arg4) + { + } + } + + [Kept] + [KeptMember(".ctor()")] + [KeptBaseType(typeof(Attribute))] + [KeptAttributeAttribute(typeof(AttributeUsageAttribute))] + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class WithArgumentsWithGenericWithDuplicatesExplicitAssertAttribute : Attribute + { + [Kept] + public WithArgumentsWithGenericWithDuplicatesExplicitAssertAttribute(object arg1, int arg2, string arg3, Type arg4) + { + } + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/TestFramework/VerifyLocalsAreChanged.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/TestFramework/VerifyLocalsAreChanged.cs new file mode 100644 index 00000000000000..e2737e15bbd82a --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/TestFramework/VerifyLocalsAreChanged.cs @@ -0,0 +1,44 @@ +using System; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.TestFramework +{ + [SetupLinkerSubstitutionFile("VerifyLocalsAreChanged.xml")] + public class VerifyLocalsAreChanged + { + public static void Main() + { + TestMethod_1(); + + TestMethod_2(); + } + + [Kept] + struct NestedType + { + public NestedType(int arg) + { + throw new NotImplementedException(); + } + } + + [Kept] + [ExpectBodyModified] + [ExpectLocalsModified] + static NestedType TestMethod_1() + { + var value = new NestedType(42); + return value; + } + + [Kept] + [ExpectedLocalsSequence(["Mono.Linker.Tests.Cases.TestFramework.VerifyLocalsAreChanged/NestedType"])] + [ExpectBodyModified] + static NestedType TestMethod_2() + { + var value = new NestedType(2); + return value; + } + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/TestFramework/VerifyLocalsAreChanged.xml b/src/tools/illink/test/Mono.Linker.Tests.Cases/TestFramework/VerifyLocalsAreChanged.xml new file mode 100644 index 00000000000000..aa7f20ee18b917 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/TestFramework/VerifyLocalsAreChanged.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs index 05f0c47812be1b..28efa01c1d88a0 100644 --- a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs +++ b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs @@ -126,13 +126,13 @@ protected virtual IEnumerable VerifyModule(ModuleDefinition original, Mo var expected = original.Assembly.MainModule.AllDefinedTypes() .SelectMany(t => GetCustomAttributeCtorValues(t, nameof(KeptModuleReferenceAttribute))) - .ToHashSet(); + .ToArray(); var actual = linked.ModuleReferences .Select(name => name.Name) - .ToHashSet(); + .ToArray(); - if (!expected.SetEquals(actual)) + if (!expected.SequenceEqual(actual)) yield return $"In module {original.FileName} Expected module references `{string.Join(", ", expected)}` but got `{string.Join(", ", actual)}`"; foreach (var err in VerifyCustomAttributes(original, linked)) @@ -832,7 +832,7 @@ public static IEnumerable VerifyBodyProperties(MethodDefinition src, Met if (src.CustomAttributes.Any(attr => attr.AttributeType.Name == expectModifiedAttributeName)) { - if (linkedValues.ToHashSet().SetEquals(srcValues.ToHashSet())) + if (linkedValues.SequenceEqual(srcValues)) { yield return $"Expected method `{src} to have it's {propertyDescription} modified, however, the {propertyDescription} were the same as the original\n{FormattingUtils.FormatSequenceCompareFailureMessage(linkedValues, srcValues)}"; } @@ -840,14 +840,14 @@ public static IEnumerable VerifyBodyProperties(MethodDefinition src, Met else if (expectedSequenceAttribute != null) { var expected = getExpectFromSequenceAttribute(expectedSequenceAttribute).ToArray(); - if (!linkedValues.ToHashSet().SetEquals(expected.ToHashSet())) + if (!linkedValues.SequenceEqual(expected)) { yield return $"Expected method `{src} to have it's {propertyDescription} modified, however, the sequence of {propertyDescription} does not match the expected value\n{FormattingUtils.FormatSequenceCompareFailureMessage2(linkedValues, expected, srcValues)}"; } } else { - if (!linkedValues.ToHashSet().SetEquals(srcValues.ToHashSet())) + if (!linkedValues.SequenceEqual(srcValues)) { yield return $"Expected method `{src} to have it's {propertyDescription} unchanged, however, the {propertyDescription} differ from the original\n{FormattingUtils.FormatSequenceCompareFailureMessage(linkedValues, srcValues)}"; } @@ -859,7 +859,7 @@ IEnumerable VerifyReferences(AssemblyDefinition original, AssemblyDefini var expected = original.MainModule.AllDefinedTypes() .SelectMany(t => GetCustomAttributeCtorValues(t, nameof(KeptReferenceAttribute))) .Select(ReduceAssemblyFileNameOrNameToNameOnly) - .ToHashSet(); + .ToArray(); /* - The test case will always need to have at least 1 reference. @@ -870,14 +870,14 @@ IEnumerable VerifyReferences(AssemblyDefinition original, AssemblyDefini Once 1 kept reference attribute is used, the test will need to define all of of it's expected references */ - if (expected.Count == 0) + if (expected.Length == 0) yield break; var actual = linked.MainModule.AssemblyReferences .Select(name => name.Name) - .ToHashSet(); + .ToArray(); - if (!expected.SetEquals(actual)) + if (!expected.SequenceEqual(actual)) yield return $"Expected references `{string.Join(", ", expected)}` do not match actual references `{string.Join(", ", actual)}`"; } @@ -918,7 +918,7 @@ IEnumerable VerifyExportedTypes(AssemblyDefinition original, AssemblyDef var expectedTypes = original.MainModule.AllDefinedTypes() .SelectMany(t => GetCustomAttributeCtorValues(t, nameof(KeptExportedTypeAttribute)).Select(l => l.FullName)); - if (!linked.MainModule.ExportedTypes.Select(l => l.FullName).ToHashSet().SetEquals(expectedTypes.ToHashSet())) + if (!linked.MainModule.ExportedTypes.Select(l => l.FullName).SequenceEqual(expectedTypes)) yield return $"Exported types do not match expected."; } @@ -961,33 +961,54 @@ protected virtual IEnumerable VerifyPseudoAttributes(EventDefinition src protected virtual IEnumerable VerifyCustomAttributes(ICustomAttributeProvider src, ICustomAttributeProvider linked) { - var expectedAttrs = GetExpectedAttributes(src).ToHashSet(); - var linkedAttrs = FilterLinkedAttributes(linked).ToHashSet(); - if (!linkedAttrs.SetEquals(expectedAttrs)) + var expectedAttrs = GetExpectedAttributes(src).ToArray(); + var linkedAttrs = FilterLinkedAttributes(linked).ToList(); + + var missingExpected = new List(); + foreach (var attr in expectedAttrs) { - var missing = $"Missing: {string.Join(", ", expectedAttrs.Except(linkedAttrs))}"; - var extra = $"Extra: {string.Join(", ", linkedAttrs.Except(expectedAttrs))}"; - string name = src switch + var match = linkedAttrs.FirstOrDefault(l => + { + if (l.TypeFullName != attr.TypeFullName) + return false; + + if (attr.CheckParameters) + return attr.ParameterValues.SequenceEqual(l.ParameterValues); + + return true; + }); + if (match != null) + linkedAttrs.Remove(match); + else + missingExpected.Add(attr); + } + + if (missingExpected.Count > 0) + yield return $"Missing custom attributes on `{GetNameForSource()}`:\n{string.Join(Environment.NewLine, missingExpected.Select(a => $" {a}").ToArray())}"; + + if (linkedAttrs.Count > 0) + yield return $"Extra custom attributes on `{GetNameForSource()}`:\n{string.Join(Environment.NewLine, linkedAttrs.Select(a => $" {a}").ToArray())}"; + + string GetNameForSource() + { + return src switch { MethodReturnType m => $"return type of '{m.Method}'", ParameterDefinition p => $"parameter '{p}' of method {p.Method}", GenericParameter g => $"generic parameter '{g}' of {g.Owner}", _ => src.ToString() }; - - yield return string.Join(Environment.NewLine, $"Custom attributes on `{name}` are not matching:", missing, extra); } } protected virtual IEnumerable VerifySecurityAttributes(ICustomAttributeProvider src, ISecurityDeclarationProvider linked) { var expectedAttrs = GetCustomAttributeCtorValues(src, nameof(KeptSecurityAttribute)) - .Select(attr => attr.ToString()) - .ToHashSet(); + .Select(attr => attr.ToString()).ToArray(); - var linkedAttrs = FilterLinkedSecurityAttributes(linked).ToHashSet(); + var linkedAttrs = FilterLinkedSecurityAttributes(linked).ToArray(); - if (!linkedAttrs.SetEquals(expectedAttrs)) + if (!linkedAttrs.SequenceEqual(expectedAttrs)) { var missing = $"Missing: {string.Join(", ", expectedAttrs.Except(linkedAttrs))}"; var extra = $"Extra: {string.Join(", ", linkedAttrs.Except(expectedAttrs))}"; @@ -1116,10 +1137,28 @@ static bool IsLdtokenOnPrivateImplementationDetails(TypeDefinition privateImplem return false; } - protected static IEnumerable GetExpectedAttributes(ICustomAttributeProvider original) + protected static IEnumerable GetExpectedAttributes(ICustomAttributeProvider original) { - foreach (var expectedAttrs in GetCustomAttributeCtorValues(original, nameof(KeptAttributeAttribute))) - yield return expectedAttrs.ToString(); + foreach (var expectedAttrs in original.CustomAttributes.Where(w => w.AttributeType.Name == nameof(KeptAttributeAttribute))) + { + if (expectedAttrs.Constructor.Parameters.Count == 1) + yield return new ExpectedAttributeComparisonInfo(false, (expectedAttrs.ConstructorArguments[0].Value as object).ToString(), Array.Empty()); + else if (expectedAttrs.Constructor.Parameters.Count == 2) + { + var expectedParameters = ((CustomAttributeArgument[])expectedAttrs.ConstructorArguments[1].Value).Select(a => + { + var arg = (CustomAttributeArgument)a.Value; + if (arg.Value == null) + return "null"; + return arg.Value.ToString(); + }).ToArray(); + yield return new ExpectedAttributeComparisonInfo(true, (expectedAttrs.ConstructorArguments[0].Value as object).ToString(), expectedParameters); + } + else + { + throw new ArgumentException($"Unhandled {nameof(KeptAttributeAttribute)} constructor with {expectedAttrs.Constructor.Parameters.Count} parameters."); + } + } // The name of the generated fixed buffer type is a little tricky. // Some versions of csc name it `e__FixedBuffer0` @@ -1132,7 +1171,7 @@ protected static IEnumerable GetExpectedAttributes(ICustomAttributeProvi Assert.Fail($"Could not locate original fixed field for {srcDefinition}"); foreach (var additionalExpectedAttributesFromFixedField in GetCustomAttributeCtorValues(fixedField, nameof(KeptAttributeOnFixedBufferTypeAttribute))) - yield return additionalExpectedAttributesFromFixedField.ToString(); + yield return new ExpectedAttributeComparisonInfo(false, additionalExpectedAttributesFromFixedField.ToString(), Array.Empty()); } } @@ -1141,7 +1180,7 @@ protected static IEnumerable GetExpectedAttributes(ICustomAttributeProvi /// /// /// - protected virtual IEnumerable FilterLinkedAttributes(ICustomAttributeProvider linked) + protected virtual IEnumerable FilterLinkedAttributes(ICustomAttributeProvider linked) { foreach (var attr in linked.CustomAttributes) { @@ -1173,7 +1212,9 @@ protected virtual IEnumerable FilterLinkedAttributes(ICustomAttributePro break; } - yield return attr.AttributeType.FullName; + yield return new ActualAttributeComparisonInfo( + attr.AttributeType.FullName, + GetCustomAttributeConstructorArgumentValues(attr).Select(p => p == null ? "null" : p.ToString()).ToArray()); } } @@ -1327,17 +1368,17 @@ IEnumerable VerifyGenericParameterConstraints(GenericParameter src, Gene // C# doesn't have syntax for annotating generic parameter constraints with arbitrary attributes, // so expected attributes on generic parameter constraints are specified on the generic parameter itself. - HashSet<(string ConstraintType, string AttributeType)> expectedConstraintAttributes = src.CustomAttributes + (string ConstraintType, string AttributeType)[] expectedConstraintAttributes = src.CustomAttributes .Where(a => IsKeptAttributeOnConstraint(a)) .Select(a => (a.ConstructorArguments[0].Value.ToString(), a.ConstructorArguments[1].Value.ToString())) - .ToHashSet(); + .ToArray(); - HashSet<(string ConstraintType, string AttributeType)> linkedConstraintAttributes = linked.Constraints + (string ConstraintType, string AttributeType)[] linkedConstraintAttributes = linked.Constraints .Where(c => c.HasCustomAttributes) .SelectMany(c => c.CustomAttributes.Select(a => (c.ConstraintType.FullName, a.AttributeType.FullName))) - .ToHashSet(); + .ToArray(); - if (!expectedConstraintAttributes.SetEquals(linkedConstraintAttributes)) + if (!expectedConstraintAttributes.SequenceEqual(linkedConstraintAttributes)) { var missing = $"Missing: {string.Join(", ", expectedConstraintAttributes.Except(linkedConstraintAttributes).Select(c => $"{c.AttributeType} on {c.ConstraintType}"))}"; var extra = $"Extra: {string.Join(", ", linkedConstraintAttributes.Except(expectedConstraintAttributes).Select(c => $"{c.AttributeType} on {c.ConstraintType}"))}"; @@ -1492,5 +1533,66 @@ protected static IEnumerable GetStringArrayAttributeValue(CustomAttribut { return ((CustomAttributeArgument[])attribute.ConstructorArguments[0].Value)?.Select(arg => arg.Value.ToString()); } + + protected static IEnumerable GetCustomAttributeConstructorArgumentValues(CustomAttribute attribute) + { + foreach (var argument in attribute.ConstructorArguments) + { + if (argument.Value is CustomAttributeArgument nestedArgument) + yield return nestedArgument.Value; + else + yield return argument.Value; + } + } + + protected class ExpectedAttributeComparisonInfo + { + public readonly bool CheckParameters; + public readonly string TypeFullName; + public readonly string[] ParameterValues; + + public ExpectedAttributeComparisonInfo(bool checkParameters, string typeFullName, string[] parameterValues) + { + if (string.IsNullOrEmpty(typeFullName)) + throw new ArgumentNullException(nameof(typeFullName)); + if (parameterValues == null) + throw new ArgumentNullException(nameof(parameterValues)); + CheckParameters = checkParameters; + TypeFullName = typeFullName; + ParameterValues = parameterValues; + } + + public override string ToString() + { + if (ParameterValues.Length == 0) + return TypeFullName; + + return $"{TypeFullName}({string.Join(",", ParameterValues)})"; + } + } + + protected class ActualAttributeComparisonInfo + { + public readonly string TypeFullName; + public readonly string[] ParameterValues; + + public ActualAttributeComparisonInfo(string typeFullName, string[] parameterValues) + { + if (string.IsNullOrEmpty(typeFullName)) + throw new ArgumentNullException(nameof(typeFullName)); + if (parameterValues == null) + throw new ArgumentNullException(nameof(parameterValues)); + TypeFullName = typeFullName; + ParameterValues = parameterValues; + } + + public override string ToString() + { + if (ParameterValues.Length == 0) + return TypeFullName; + + return $"{TypeFullName}({string.Join(",", ParameterValues)})"; + } + } } }